Skip to content

[BUG] "Protocol error (Target.createTarget): Target closed" after calling BrowserContext.newPage #298

@gpoole

Description

@gpoole

Environment

  • chromium Version: 127
  • puppeteer / puppeteer-core Version: 23.1.1
  • Node.js Version: 20.x
  • Lambda / GCF Runtime: (Unsure, using Netlify Functions)

Note: My function is a Next.js route handler function running in Netlify which I believe is AWS Lambda.

Current Behavior

An error is thrown when calling BrowserContext.newPage via Puppeteer. The error is:

TargetCloseError: Protocol error (Target.createTarget): Target closed
Full error trace
TargetCloseError: Protocol error (Target.createTarget): Target closed
    at CallbackRegistry.clear (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:72:36)
    at #onClose (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Connection.js:164:25)
    at WebSocket.<anonymous> (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/node/NodeWebSocketTransport.js:49:30)
    at callListener (/var/task/node_modules/ws/lib/event-target.js:290:14)
    at WebSocket.onClose (/var/task/node_modules/ws/lib/event-target.js:220:9)
    at WebSocket.emit (node:events:519:28)
    at WebSocket.emitClose (/var/task/node_modules/ws/lib/websocket.js:272:10)
    at Socket.socketOnClose (/var/task/node_modules/ws/lib/websocket.js:1341:15)
    at Socket.emit (node:events:519:28)
    at TCP.<anonymous> (node:net:339:12) {
  cause: ProtocolError
      at <instance_members_initializer> (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:93:14)
      at new Callback (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:97:16)
      at CallbackRegistry.create (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/common/CallbackRegistry.js:22:26)
      at Connection._rawSend (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Connection.js:89:26)
      at Connection.send (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Connection.js:80:21)
      at CdpBrowser._createPageInContext (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Browser.js:180:53)
      at CdpBrowserContext.newPage (/var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/BrowserContext.js:115:40)
      at async w (/var/task/packages/frontend/.next/server/app/<redacted>/route.js:1:1624)
      at async v (/var/task/packages/frontend/.next/server/app/<redacted>/route.js:1:2092)
      at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:36258
}

Steps to Reproduce

The following code triggers the error:

import { isDev, isNetlify } from "@/lib/environment";
import serverlessChromium from "@sparticuz/chromium";
import puppeteer, {
  type BrowserContext,
  type CookieParam,
  type PDFOptions,
  type PuppeteerLaunchOptions,
} from "puppeteer-core";

export default async function htmlToPdf(
  url: string,
  options: PDFOptions = {},
  cookies?: CookieParam[],
) {
  let context: BrowserContext | null = null;
  try {
    let launchOptions: PuppeteerLaunchOptions | undefined;
    if (isNetlify()) {
      console.log("Using @sparticuz/chromium");
      launchOptions = {
        defaultViewport: serverlessChromium.defaultViewport,
        args: serverlessChromium.args,
        executablePath: await serverlessChromium.executablePath(),
        headless: serverlessChromium.headless,
      };
    } else if (isDev()) {
      launchOptions = {
        args: puppeteer.defaultArgs(),
        channel: "chrome",
        headless: true,
      };
    } else {
      throw new Error("Unsupported environment");
    }

    const browser = await puppeteer.launch(launchOptions);
    context = await browser.createBrowserContext();

    // This line throws: TargetCloseError: Protocol error (Target.createTarget): Target closed
    const page = await context.newPage();

    if (cookies != null) {
      await page.setCookie(...cookies);
    }

    await page.goto(url);

    const pdfBuffer = await page.pdf(options);

    return Buffer.from(pdfBuffer);
  } finally {
    context?.close();
  }
}

If I modify the code to directly use the default browser context and skip the call to createBrowserContext, it works as expected. The following changes make the code work:

--- broken.ts   2024-09-11 10:02:52
+++ working.ts  2024-09-11 10:03:18
@@ -4,7 +4,7 @@
   type PuppeteerLaunchOptions,
   type PDFOptions,
   type CookieParam,
-  type BrowserContext,
+  type Browser,
 } from "puppeteer-core";
 
 export default async function htmlToPdf(
@@ -12,7 +12,7 @@
   options: PDFOptions = {},
   cookies?: CookieParam[],
 ) {
-  let context: BrowserContext | null = null;
+  let browser: Browser | null = null;
   try {
     let launchOptions: PuppeteerLaunchOptions | undefined;
     if (isNetlify()) {
@@ -33,10 +33,9 @@
       throw new Error("Unsupported environment");
     }
 
-    const browser = await puppeteer.launch(launchOptions);
-    context = await browser.createBrowserContext();
+    browser = await puppeteer.launch(launchOptions);
 
-    const page = await context.newPage();
+    const page = await browser.newPage();
 
     if (cookies != null) {
       await page.setCookie(...cookies);
@@ -48,6 +47,6 @@
 
     return Buffer.from(pdfBuffer);
   } finally {
-    context?.close();
+    browser?.close();
   }
 }

I've seen comments about the memory size of the functions being a potential problem so I've checked and Netlify functions have 1024mb of memory available. The memory usage logs I'm seeing show a maximum 600mb being used.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions