Encountering an issue with AWS CDK TypeScript Lambda: "The function chromium.executablePath is not recognized" when using Puppeteer and a Custom Chromium Layer

Challenges Faced with AWS CDK, TypeScript Lambda, and Lambda Layers - Issue: chromium.executablePath Not Functioning Properly

Currently, I am in the process of developing an AWS Lambda function using TypeScript along with AWS CDK for its deployment. The main functionality of my Lambda function involves utilizing Puppeteer and a customized Chromium build to generate PDF files. Unfortunately, I have encountered a TypeError specifically related to the operation of chromium.executablePath.

Dependencies within Layer:

In relation to the Lambda Layer, my package.json lists the following dependencies:

"dependencies": {
  "@sparticuz/chromium": "^106.0.2",
  "puppeteer-core": "^18.0.5"
}

Lambda Function Implementation:

This excerpt showcases the relevant section from my Lambda function:

const puppeteer = require("/opt/nodejs/puppeteer-core");
const chromium = require("/opt/nodejs/@sparticuz/chromium");

async function createPdf(fileLinks: Record<string, string>, logoDataUri: string, bucketName: string, key: string): Promise<Buffer> {
  await chromium.font("https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf"); // Add Font

  const browser = await puppeteer.launch({
    args: chromium.args,
    defaultViewport: chromium.defaultViewport,
    executablePath: await chromium.executablePath(),
    headless: chromium.headless,
  });

  // ... remaining functions
}

// Continuing logic for the function...

Error Encounter Details:

Following the Lambda invocation, the error received is as follows:

{
  "errorType": "TypeError",
  "errorMessage": "chromium.executablePath is not a function",
  "trace": [
    "TypeError: chromium.executablePath is not a function",
    "    at createPdf (/var/task/index.js:70735:36)",
    "    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)",
    "    at async Runtime.handler (/var/task/index.js:70601:21)"
  ]
}

CDK Setup Definitions:

The layer and Lambda function definitions in my CDK stack are configured as follows:

this.chromiumLayer = new LayerVersion(this, "chromiumLayer", {
  code: Code.fromAsset(join(__dirname, "../../src/layers/chromium")),
  compatibleRuntimes: [Runtime.NODEJS_20_X],
  compatibleArchitectures: [Architecture.X86_64, Architecture.ARM_64],
  description: "Chromium layer for Lambda",
});

this.createDownloadPDF = new NodejsFunction(this, "createDownloadPDF", {
  // ... other configurations
  layers: [this.chromiumLayer],
  bundling: {
    externalModules: ["aws-sdk", "@sparticuz/chromium", "puppeteer-core"],
    // ... other bundling options
  },
});

tsconfig Entry:

"paths": {
  "/opt/nodejs/puppeteer-core": ["./src/layers/chromium/nodejs/node_modules/puppeteer-core"],
  "/opt/nodejs/@sparticuz/chromium": ["./src/layers/chromium/nodejs/node_modules/@sparticuz/chromium"]
}

Queries:

  1. Why is the TypeError regarding chromium.executablePath occurring?
  2. Is there a specific method recommended to bundle or configure Lambda layers when using AWS CDK in this scenario?
  3. Are additional steps necessary to ensure proper integration of @sparticuz/chromium with Puppeteer within a Lambda environment?

Your insights and potential solutions to resolve this issue will be highly appreciated. Thank you!

Answer №1

Revelations

My exploration led me to the realization that utilizing a NodejsFunction presented certain challenges, which I successfully addressed by incorporating a JS lambda function.

Solution in Action:

CDK Code:

this.chromiumLayer = new LayerVersion(this, "chromiumLayer", {
  code: Code.fromAsset(join(__dirname, "../../src/layers/chromium")),
  compatibleRuntimes: [Runtime.NODEJS_18_X],
  compatibleArchitectures: [Architecture.X86_64, Architecture.ARM_64],
  description: "Chromium layer for Lambda",
});

this.createDownloadPDF = new aws_lambda.Function(this, "createDownloadPDF", {
  runtime: aws_lambda.Runtime.NODEJS_18_X,
  code: aws_lambda.Code.fromAsset(join(__dirname, "./createDownloadPDF")),
  handler: "index.handler",
  memorySize: 1024,
  logRetention: RetentionDays.ONE_WEEK,
  timeout: Duration.minutes(10),
  retryAttempts: 2,
  description: "Create the instruction PDF for users to download from Etsy",
  environment: {
    OUTPUT_BUCKET: importedBucketName,
  },
  layers: [this.chromiumLayer],
});

Lambda JS Code:

const puppeteer = require("/opt/nodejs/node_modules/puppeteer-core");
const chromium = require("/opt/nodejs/node_modules/@sparticuz/chromium");

async function createPdf(fileLinks, logoDataUri, bucketName, key) {
  let browser;
  try {
    // Launching browser
    browser = await puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath(),
      headless: chromium.headless,
    });

    const page = await browser.newPage();

}

Lambda Layer Package.json:

  "dependencies": {
    "@sparticuz/chromium": "119.0.2",
    "puppeteer-core": "21.5.0"
  },

Answer №2

After trying numerous methods, I found that the most effective solution for using Nodejs.18+ with Chromium binaries was to download them at runtime.

This approach eliminates the need for additional layers, external dependencies, or any other complications. However, the downside is the cold start: your lambda function will now have to retrieve and unpack a ~58MB binary from a remote source (I suggest storing it in S3).

You can read more about this solution here:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

tsc is not able to identify virtual fields in a mongoose schema

I'm having trouble getting mongoose virtual to work in typescript. Following the instructions outlined in "another approach" on mongoose documentation, I used mongoose's InferSchemaType to create the interface. However, TSC does not recognize t ...

There was a problem with Type TS2507: The Type 'typeof Tapable' cannot be used as a constructor function type

After creating my own TypeScript library for shared TS models, I wanted to incorporate it into a couple of other projects I'm working on. Here are the essential parts of the library repository: index.ts: export interface IApp { ... } package.json: ...

TSX implementation of a paginator with an ellipse in the center

Looking to add ellipses in the Pagination, specifically when there are more than 10 pages (e.g., 1 2 3 4 ... 11 12 13 14). I've tried various methods but need guidance as a beginner. Can anyone suggest changes based on my code to help me achieve this? ...

The Power of Asynchronous Programming with Node.js and Typescript's Async

I need to obtain an authentication token from an API and then save that token for use in future API calls. This code snippet is used to fetch the token: const getToken = async (): Promise<string | void> => { const response = await fetch(&apos ...

Tips on delaying the return of the Angular compiler until the subscription is complete

I'm facing an issue with a function that needs to return a value while containing a subscription. The problem I'm encountering is that the return line is being executed before the subscription ends, testFunc(){ let objectType; let modul ...

Utilize Typescript Functions to Interact with GeoJSON Data in Palantir Foundry

Working on a map application within Palantir utilizing the workshop module. My goal is to have transport routes showcased along roads depending on user inputs. I am familiar with displaying a route using GeoJSON data, but I'm wondering if there is a w ...

What could be causing the issue with my customized sorting in the Material-UI Data Grid component?

After integrating Material-UI's Data Grid Component with my API's JSON array, I had to create a RenderCell function to handle text overflow and include a button that directs Users to a different page. Additionally, I utilized the ValueGetter for ...

Can a custom structural directive be utilized under certain circumstances within Angular?

Presently, I am working with a unique custom structural directive that looks like this: <div *someDirective>. This specific directive displays a div only when certain conditions are met. However, I am faced with the challenge of implementing condit ...

What is the method for retrieving context data using useContext in the primary/overall component?

Check out the codesandbox for this code. I have been able to successfully pass and update context data within the child components. However, I am facing an issue where I am unable to access this data in the parent component. Any insights on why this might ...

Error TS2322 occurs during compilation in Typescript when using ng.IPromise

Having some issues while using Angular 1.x with Typescript. Here is the code causing trouble: get(id): ng.IPromise<Server.MyItem> { return this.$http.get(`${this.baseAddress}/${id}`).then(d => d.data); } After compiling with tsc, I am encoun ...

Avoiding the use of reserved keywords as variable names in a model

I have been searching everywhere and can't find a solution to my unique issue. I am hoping someone can help me out as it would save me a lot of time and effort. The problem is that I need to use the variable name "new" in my Typescript class construct ...

What steps do I need to take in order to activate scrolling in a Modal using Material-UI

Can a Modal be designed to work like a Dialog with the scroll set to 'paper'? I have a large amount of text to show in the Modal, but it exceeds the browser window's size without any scrolling option. ...

Utilizing the useSearchParams() function to retrieve live data in Next.js

Is there anyone who has successfully migrated from the pages router to the app router in Next.js? I am facing an issue with dynamic data migration. In the pages router, dynamic data is retrieved on a page using useRouter().query, but in the app router, it ...

"Creating a Typescript function that guarantees a non-null and non-undefined return

Currently, I am working on developing a function that is designed to return a specific value. In the event that the returned value is null or undefined, the function should then default to a pre-determined value. function test<A, B>(input: A, fallba ...

displaying post data in the URL address bar

During the development of my portal using angular 5, everything was running smoothly in the testing environment. However, due to production requirements, I made some changes to access modifiers from private to public. This modification has caused issues in ...

Unable to call a function without a call signature. The type 'moment' does not have any compatible call signatures at position (4,1). TS2349 error needs resolution

I encountered issues with Typescript 3.* and momentjs when working with dates, resulting in compile time errors. Error:(3, 13) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof moment' has no compatible call s ...

Errors slipping through the cracks of Express middleware

When it comes to using express and typescript, I am attempting to create a middleware for error handling similar to Sentry. Below is the code snippet: const catchError = ( error: Error, req: Request, _res: Response, next: any ) => { console. ...

Trouble updating React Context state when attempting to utilize token from cookies

From my understanding, the typical method of maintaining context state in a React web application involves storing the user's information in a token within local storage or a cookie. Upon each page load, this token is retrieved to reset the context st ...

Adding a fresh element and removing the initial item from a collection of Objects in JavaScript

I'm working on creating an array of objects that always has a length of five. I want to push five objects initially, and once the array reaches a length of five, I need to pop the first object and push a new object onto the same array. This process sh ...

What is the reason behind TypeScript failing to provide type safety in a generic higher order component which introduces extra properties into React components?

I'm currently working on developing a versatile higher order component, but have encountered an issue with type safety not being enforced. Interestingly, when attempting the same implementation without using generics, the type safety is verified as ex ...