Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Debug Puppeteer by first identifying which layer is failing: your Node.js script, code running in the page, the Chrome process, or communication between Puppeteer and Chrome. Then collect evidence from that layer. A visible browser and a saved screenshot expose rendering and timing problems; browser DevTools and Node’s inspector let you pause at the relevant code; protocol logs and browser-process output help explain hangs and launch failures.

Start by identifying the failing layer

Puppeteer sits between a Node.js program and a browser, while the page itself runs its own JavaScript and makes network requests. A failure that looks like “Puppeteer is stuck” may therefore come from different places. The Puppeteer project’s debugging guide notes that there is no single debugging method for every issue because the system spans browser requests, Web APIs, Node.js code, and browser internals.

  • Node.js orchestration: the script did not reach an awaited call, is waiting on the wrong condition, or has an exception in its own logic.
  • Page code or rendering: the page failed to load or render as expected, a client-side script threw, or a selector is absent.
  • Browser process: Chrome or Chromium cannot start, exits unexpectedly, or reports an environment problem.
  • DevTools Protocol: Puppeteer sent a browser command that has not returned, or the connection between Node and the browser is unhealthy.

Write down the last operation that completed and the first one that did not. That boundary is more useful than treating every timeout as a generic Puppeteer error.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make the browser’s behavior visible

For an intermittent click, navigation, or typing issue, start with the simplest observation: run the browser with its UI visible and slow the automation down. This is a diagnostic setup, not a production setting.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    slowMo: 250,
  });

  try {
    const page = await browser.newPage();
    await page.goto('https://example.com');
    await page.screenshot({ path: 'debug.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Run it with node script.js. The visible window shows whether the browser reached the expected page and whether an overlay, redirect, or unexpected state changed what the automation sees. slowMo adds a delay between Puppeteer operations, making sequences easier to watch; it also makes the run slower, so remove it after diagnosis. A screenshot preserves the rendered state for later inspection, including cases that are too brief to catch by eye.

Debug JavaScript running inside the page

Code passed to page.evaluate() runs in the browser context, not in Node.js. As a result, a browser-side console.log() does not automatically appear in your terminal. Forward page console messages explicitly:

page.on('console', msg => console.log('PAGE LOG:', msg.text()));
page.on('pageerror', error => console.error('PAGE ERROR:', error));

await page.evaluate(() => {
  console.log(`url is ${location.href}`);
});

Register these listeners before navigation or evaluation so early messages are not missed. The pageerror listener is useful for uncaught exceptions; console forwarding makes deliberate logging visible in Node. For failed requests, add a request-failure listener when network behavior is relevant:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
page.on('requestfailed', request => {
  console.error('REQUEST FAILED:', request.url(), request.failure()?.errorText);
});

To pause at a specific point in browser-side code, launch with devtools: true and put a debugger statement inside the function evaluated in the page:

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
const browser = await puppeteer.launch({
  headless: false,
  devtools: true,
});
const page = await browser.newPage();

await page.evaluate(() => {
  debugger;
  console.log('The page debugger can pause here');
});

When execution reaches debugger, Chrome DevTools can pause and let you inspect the page’s variables and execution state. This is the right tool for DOM, browser API, and client-side logic questions; it will not step through the Node.js code that called page.evaluate().

Debug the Node.js script with the inspector

Use Node’s inspector when the issue is in orchestration: for example, a script does not reach page.click(), uses the wrong variable, or appears to wait at an await. Put a debugger; statement in the Node code, launch the test browser headful, then start the script with:

node --inspect-brk path/to/script.js
  1. Open chrome://inspect/#devices in Chrome.
  2. Find the Node target and click inspect.
  3. In the DevTools window, press F8 to resume from the initial pause.
  4. Use the debugger controls to step through the script and inspect local variables around the failing operation.

--inspect-brk pauses Node at startup, which gives you time to attach the debugger. With the browser visible as well, you can correlate a Node call with what the page is doing. If the test works only when the inspector is attached, consider whether the extra time changes a race or timeout rather than assuming the debugger fixed the underlying issue.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Investigate hangs and protocol calls

If an asynchronous Puppeteer operation never resolves and ordinary logging does not identify the boundary, enable Puppeteer’s internal debug output:

env NODE_DEBUG="puppeteer:*" node script.js

This logs internal Puppeteer and DevTools Protocol activity, which can help establish whether a command was sent and whether a response arrived. The output can be large and may contain sensitive information. Avoid publishing it unredacted, especially if the run uses authenticated pages, cookies, or private URLs.

For unresolved protocol calls, inspect browser.debugInfo.pendingProtocolErrors. The pending errors include stack traces pointing to the code that initiated the protocol call. Use that stack to locate the operation in your script, then compare it with the last protocol messages in the debug log. The goal is to determine whether your code is awaiting a condition that never becomes true or whether the browser command itself remains outstanding.

Find browser launch and crash failures

When Chrome fails to launch or exits unexpectedly, forward its output to the Node process rather than looking only at the Puppeteer exception:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const browser = await puppeteer.launch({ dumpio: true });

dumpio: true forwards browser logs to Node’s standard streams. Preserve the full error and stack trace, and record the Puppeteer version, browser version, operating system, and operation being attempted. Those details help distinguish an install problem from a browser incompatibility or an application bug.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Check that the browser was installed where Puppeteer expects

Since Puppeteer v19, its downloaded browsers normally use ~/.cache/puppeteer. If an application runs under a different user, in a container, or with a custom cache location, the browser may not be present in the environment that launches the script. PUPPETEER_CACHE_DIR can relocate the cache. Confirm that the install and runtime use the same configured location.

Check whether the package manager blocked installation

Some package-manager configurations prevent package install scripts from running, which can leave Puppeteer installed without its browser download. Run npx puppeteer browsers install in the project environment or allow the Puppeteer install script, then retry. Do not assume a successful npm package installation proves a usable browser binary is available.

Check platform permissions and compatibility

  • Windows sandbox permissions: newer Puppeteer versions attempt setup automatically, but older versions or restricted environments may still need executable permission fixes. Use the guidance for the exact version and environment rather than copying a permission workaround blindly.
  • Alpine Linux: Chrome is not supported out of the box. Chromium and Puppeteer versions need to be compatible. The Puppeteer troubleshooting guide has also noted a Chromium 3.20 timeout issue with a 3.19 downgrade workaround for the version context described there; treat that as a version-specific compatibility case, not a general Alpine fix.
  • Extensions and managed policies: Puppeteer disables extensions by default. If a managed Chrome policy requires extensions, the launch configuration may need enableExtensions: true.

Version-specific workarounds age quickly. Match any troubleshooting advice to your installed Puppeteer and browser versions, and prefer correcting the underlying environment over adding launch flags that mask the actual cause.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture evidence for rendering and performance problems

Use a screenshot when you need to see what the page looked like at the failure point. Save it immediately after the operation under investigation or after a catch block that records the current URL and state. A full-page capture can reveal content farther down the document; a viewport capture is often more useful for a particular interaction. Screenshots may contain personal or confidential page data, so handle and store them accordingly.

For a slow page or a sequencing problem that is hard to explain from logs, record a trace:

await page.tracing.start({ path: 'trace.json' });
try {
  await page.goto('https://example.com');
  await page.waitForSelector('main');
} finally {
  await page.tracing.stop();
}

The resulting trace can be opened in Chrome DevTools or a timeline viewer to inspect the sequence and timing of browser activity. Tracing adds overhead and creates a file that may contain details about the page and its requests. Keep the trace focused on the action you need to diagnose, and do not leave tracing enabled for routine runs without a reason.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choose the diagnostic by symptom

Symptom or question First technique Evidence it provides Trade-off
Clicks or navigation happen too quickly to observe headless: false and a small slowMo Visible browser state and action order Slower run; timing may differ from production
A selector or page script behaves unexpectedly Page console and page-error listeners; browser DevTools for breakpoints Client-side messages, exceptions, and paused page execution Requires a visible browser for interactive inspection
The Node script does not reach or complete an operation node --inspect-brk and chrome://inspect/#devices Call stack, variables, and control flow Attaching the debugger changes runtime timing
An awaited browser operation hangs NODE_DEBUG="puppeteer:*" and pending protocol error inspection Protocol traffic and the initiating call stack Verbose, potentially sensitive logs
Chrome crashes or will not start dumpio: true, then check installation and environment Browser-process output and launch diagnostics Output can be noisy; platform-specific causes require version checks
A visual state or timing sequence needs to be reviewed later Screenshot or tracing Saved rendering or a timeline of browser activity Artifacts take storage and may expose page data

Or skip the browser setup

If your goal is simply to capture a website rather than debug Puppeteer itself, ScreenshotNeo provides a one-request screenshot API and an MCP server for AI agents. The API can return PNG, JPEG, WebP, or PDF; its optional cleanup accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each cleanup step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One cURL request:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for authentication and available parameters. The MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Free includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up free for 1,000 screenshots a month, with no card required.

Keep debugging runs useful and safe

  • Reproduce the failure with the smallest script and the fewest unrelated browser actions.
  • Record the exact URL, last successful operation, timeout or error text, Puppeteer version, browser version, and runtime environment.
  • Use one diagnostic channel at a time where possible; a debugger, protocol logging, and tracing together can make timing slower and evidence harder to interpret.
  • Remove temporary listeners, slowMo, debug flags, and traces when the issue is resolved.
  • Redact credentials, cookies, private URLs, and personal data before sharing logs, screenshots, or trace files.

For timeouts in particular, determine which awaited operation timed out and what condition it was waiting for. Increase a timeout only when the expected operation legitimately needs more time; a longer limit does not fix an unreachable selector, blocked browser download, or stalled protocol command.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.