Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nodeHtmlToImage intermittently throws unexpected and uncatchable errors (with AWS lambda repro) #221

Open
johnpc opened this issue Feb 6, 2024 · 2 comments

Comments

@johnpc
Copy link

johnpc commented Feb 6, 2024

This issue is actually 4 issues.

  • First, when I wrap nodeHtmlToImage in a try/catch, some errors slip though going uncaught. See Catch puppeteer errors not possible? #165
  • Second, it intermittently throws an error that I don't expect: Protocol error (Page.captureScreenshot): Unable to capture screenshot
  • Third, it intermittently throws another that I don't expect: Error: Navigating frame was detached at #onFrameDetached
  • Intermittently, the screenshot taken is a blank image that does not represent the html at all (<6000 bytes)

This all can be reproduced by deploying the following to AWS Lambda using node 18 or 20 runtime:

import nodeHtmlToImage from "node-html-to-image";
import puppeteerCore from "puppeteer-core";
import chromium from "@sparticuz/chromium-min";

export const handler = async (
  event: any,
  context: any,
  callback: (err: any, response: any) => void,
) => {
  const executablePath = await chromium.executablePath(
    "https://github.com/Sparticuz/chromium/releases/download/v119.0.2/chromium-v119.0.2-pack.tar",
  )!;
  const html = `
  <html>
  <head>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
    />
  </head>
  <style>
    body {
      width: 1200px;
      height: 800px;
    }
    .content {
      position: absolute;
      left: 50%;
      top: 50%;
      -webkit-transform: translate(-50%, -50%);
      transform: translate(-50%, -50%);
    }
  </style>
  <body
    style="
      background-size: cover;
    "
  >
    <div class="content">
      <h1 style="font-size: 100px">Hello World</h1>
    </div>
  </body>
</html>
`

  try {
    for (let i = 0; i < 10; i++) {
      const nodeHtmlToImageResponse = await nodeHtmlToImage({
        type: "png",
        html,
        waitUntil: "networkidle0",
        puppeteer: puppeteerCore,
        puppeteerArgs: {
          args: [...chromium.args, "--disable-features=site-per-process",  '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--single-process', '--disable-gpu'],
          headless: true,
          executablePath,
          defaultViewport: {
            width: 1200,
            height: 800,
          },
        },
      });
      console.log({nodeHtmlToImageResponse});
    }
  } catch (e) {
    // This cannot catch the following errors:
    // > Error: Navigating frame was detached at #onFrameDetached
    // > Protocol error (Page.captureScreenshot): Unable to capture screenshot
    // Instead, these cause runtime to exit with catch block never ran
    // > Runtime exited with error: exit status 1 Runtime.ExitError

    // Additionally, sometimes there is no error but the screenshot is erroneously blank (<3000 bytes)
    const err = (e as Error).message;
    const errorResponse = {
      statusCode: 500,
      body: JSON.stringify({
        message: "screenshot failed",
        reason: err,
      }),
    };
    console.error({ errorResponse });
    return callback(null, errorResponse);
  }

  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: "screenshot successful!",
    }),
  };

  console.log({ response });
  return callback(null, response);
};

Once deployed, put the lambda on a cron trigger to run every few minutes. The errors are intermittent, but it doesn't take long to start seeing them in your lambda execution logs. Note that the catch block never hits - the unexpected failures within nodeHtmlToImage kill the lambda runtime completely without being catchable.

@ext0
Copy link

ext0 commented Mar 17, 2024

+1, running into this as well on Lambda. Did you end up finding any solution here?

@ext0
Copy link

ext0 commented Mar 18, 2024

} catch (err) {
console.error(err);
await cluster.close();
process.exit(1);

This looks to be the culprit. This library is exiting the process on unexpected errors in the cluster. This is very unexpected behavior. I'll put in a PR sometime this week if I can find the time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants