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 finding which layer failed—your code, page JavaScript, navigation or network, the DevTools protocol, the browser process, or the host environment—then collect evidence for that layer. Start with the complete error and versions, reproduce visibly with headless: false, and add protocol logs or Chrome output only when they help answer a specific question. Avoid treating every timeout as a reason to raise a global limit.

Start by locating the failing layer

Puppeteer controls Chrome or Firefox through the DevTools Protocol or WebDriver BiDi. That means a failure can happen in your script, inside the page, while loading a URL, in communication with the browser, during browser startup, or in the machine running it. The maintainers note that there is no single debugging method for all Puppeteer issues because it touches distinct browser components.

Classifying the failure first keeps you from changing unrelated settings. For example, a selector that never appears is different from Chrome failing to launch, even if both eventually surface as a timeout.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Symptom Likely layer to investigate first
Chrome does not start, exits immediately, or reports missing libraries Browser process or host environment
Navigation or a network-idle wait does not finish Network, navigation, or page behavior
A selector wait expires Page state, selector, frame, or timing
An awaited Puppeteer call stays pending DevTools protocol or browser responsiveness
It works locally but fails in CI, a container, or a cloud runtime Environment differences, permissions, resources, or lifecycle

Capture enough information to reproduce the failure

Before changing launch flags or timeouts, preserve a minimal failure report. Keep the complete error and stack trace; the operation underway when it failed; and the exact versions and options involved. Puppeteer releases are paired closely with specific browser releases for protocol compatibility, so a version mismatch can matter even when the script has not changed.

  • Full error text and stack trace, without truncating nested causes.
  • The failing operation, such as launch, navigation, waitForSelector, or PDF generation.
  • Puppeteer version, browser name and version, Node.js version, and operating system.
  • Container base image or cloud runtime, when applicable.
  • Launch options and arguments, including headless mode, sandbox flags, and profile directory.
  • The target URL and a minimal script that reproduces the failure.
  • Whether the failure is consistent, intermittent, or limited to CI/cloud execution.

Keep credentials, cookies, authorization headers, and private page content out of logs shared publicly. Browser and protocol diagnostics can include sensitive data.

Make the browser visible and inspect your script

When a failure depends on page state or timing, run non-headless to see what Chrome actually does. Add a debugger; statement immediately before the suspicious action, then start Node with its inspector paused at startup:

node --inspect-brk debug.js

Open chrome://inspect/#devices in Chrome, choose Inspect for the Node target, and press F8 to resume. Step through the script around the failing operation. With headless: false, you can also see whether navigation reaches the expected page, a consent screen, an error page, or a state in which the target element is absent.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com');
    debugger;
    await page.waitForSelector('h1');
    console.log(await page.title());
  } finally {
    await browser.close();
  }
})();

For this diagnostic run, use the same URL, selector, and important launch settings as the failing run. If the visible page is still loading, blocked, or showing different content, investigate that page or network state before changing the selector timeout.

Instrument protocol calls and browser output

Log Puppeteer protocol activity

For a call that hangs or fails without enough context, enable Puppeteer’s internal debug logging before starting the process:

NODE_DEBUG="puppeteer:*" node debug.js

Use these logs to see protocol activity surrounding the operation, not as a permanent default: the documentation warns that logs may contain sensitive information. If an asynchronous call never resolves, inspect the browser’s pending protocol errors while the browser object is still available:

console.error(browser.debugInfo.pendingProtocolErrors);

The returned Error objects include stack traces pointing to the code that initiated the protocol call. That can distinguish a stuck browser operation from an application-level promise or wait that is not completing.

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

Forward Chrome’s stdout and stderr

If Chrome crashes or fails before a page is available, launch with dumpio: true to forward Chrome’s stdout and stderr to the Node process. Capture that output alongside the Node error; it can expose startup or process failures that page-level logging cannot show.

const browser = await puppeteer.launch({
  headless: true,
  dumpio: true
});

The launch API also documents debuggingPort, pipe, devtools, userDataDir, and waitForInitialPage. Treat these as diagnosis controls: change one at a time and record the original value, since they alter how the browser is started or connected to. The API reference specifies a default launch timeout of 30,000 ms; that is the launch timeout, not a universal timeout for page navigation or selectors.

Fix Chrome launch failures by cause

Browser missing or cache not writable

Puppeteer normally downloads a compatible browser during installation. If install scripts were blocked or the expected browser is missing, install it explicitly:

npx puppeteer browsers install

If the default cache location is unsuitable for your user or container, set PUPPETEER_CACHE_DIR to a writable location and ensure the process running Puppeteer can read the installed browser files. Check installation and permissions as the same user that runs the automation; a successful download under a different account does not establish that the runtime can access it.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Browser and Puppeteer versions do not match

Check the actual browser binary being launched as well as the installed Puppeteer package. Each Puppeteer release is tightly bundled with a browser release for CDP and WebDriver BiDi compatibility. Align the browser with the version supported by the installed Puppeteer release rather than assuming that any system Chrome is interchangeable.

Linux sandbox or AppArmor blocks startup

An error such as No usable sandbox! can point to missing sandbox support or an AppArmor policy that blocks user namespaces. Prefer configuring the host’s sandbox support or policy correctly. The official troubleshooting guidance strongly discourages running without a sandbox. It documents --no-sandbox only as a trust-dependent workaround for content you absolutely trust; disabling the sandbox changes the browser’s security posture and should not be a general CI fix.

Shared libraries or distribution mismatch

Linux browser builds require system dependencies. WSL and minimal CI images may omit libraries Chrome needs, so use the environment-specific dependency guidance rather than copying a package list intended for another distribution. Alpine requires special care: Chrome does not support Alpine out of the box, and the troubleshooting guide describes Chromium/Puppeteer compatibility concerns, including a Chromium timeout issue on Alpine 3.20. Its Alpine 3.19 downgrade advice applies to that documented scenario, not every Alpine installation.

Cloud runtime slows or interrupts background work

Some cloud execution settings can make a healthy browser look stalled. The Puppeteer troubleshooting guide notes that Cloud Run can disable CPU after an HTTP response, making background Puppeteer work appear extremely slow. Complete the browser work before sending the response, or configure always-on CPU where appropriate for that platform. Confirm the runtime’s lifecycle and resource behavior before concluding that Puppeteer itself is hanging.

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

Debug navigation and selector waits as separate problems

A selector wait expires when the requested selector does not appear before the configured timeout. Inspect the live page and ask which condition prevented it from appearing: did navigation finish, is the selector correct for the rendered DOM, is the content inside another frame, or is the element rendered only after a condition or interaction?

const response = await page.goto('https://example.com', {
  waitUntil: 'domcontentloaded'
});
console.log('status:', response && response.status());
console.log('url:', page.url());
console.log('title:', await page.title());

await page.waitForSelector('h1', { timeout: 10000 });

This is a diagnostic example, not a universal navigation recipe: choose the navigation condition and selector timeout that match the page’s behavior. A selector may be absent because the wrong page loaded, the page has not rendered it yet, or the selector targets the wrong frame. An element can also be detached or conditionally rendered while automation is waiting.

Do not increase every timeout globally as a first response. First determine whether the delay belongs to navigation, a network request, a selector, or the browser process. Raising a timeout can hide a slow or incorrect condition without fixing it.

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

Or skip the browser setup

If your goal is to capture a URL as an image or PDF rather than interact with arbitrary browser state, ScreenshotNeo provides a screenshot API and MCP server. It is not a replacement for Puppeteer scripts that need custom page logic, but it can handle URL-based capture without setting up a local browser. Its clean-shot flow accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, with response headers identifying the page verdict and billing status. An MCP server exposes screenshot tools to Claude, Cursor, and other MCP clients.

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

One GET request returns an image or PDF. See the ScreenshotNeo API documentation for parameters and response details:

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

The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan. Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.

Common debugging mistakes to avoid

  • Changing several variables at once: preserve a baseline, then change one launch option, timeout, or environment condition per run so you can identify what affected the result.
  • Assuming a local pass proves CI is equivalent: compare browser installation, user permissions, dependencies, sandbox policy, container image, and cloud CPU lifecycle.
  • Using --no-sandbox as routine configuration: it avoids a security boundary rather than diagnosing why the host sandbox cannot run.
  • Sharing raw debug logs indiscriminately: redact tokens, cookies, authorization data, and sensitive URLs before sharing.
  • Calling every timeout a Puppeteer bug: record which operation timed out and inspect the page, navigation, and browser process state that operation depends on.

A practical order of operations

  1. Save the complete error, stack trace, versions, launch options, URL, and failed operation.
  2. Classify the failure as code/page, navigation/network, protocol, browser startup, or host environment.
  3. Reproduce with headless: false; use debugger;, --inspect-brk, and chrome://inspect/#devices when stepping through code will clarify page state.
  4. For a pending protocol call, enable NODE_DEBUG="puppeteer:*" and inspect browser.debugInfo.pendingProtocolErrors; for early Chrome failure, use dumpio: true.
  5. For launch issues, verify browser installation and cache permissions, version pairing, host dependencies, sandbox/AppArmor, and runtime resource behavior.
  6. For waits, inspect actual navigation and rendered state before deciding whether a selector or timeout is wrong.

Frequently Asked Questions

Where can I look up the available Puppeteer launch options?

Use the Puppeteer LaunchOptions API reference for the installed release; options and behavior can vary by version.

Can browser debug logs include private information?

Yes. Puppeteer’s debugging guidance warns that debug logs may contain sensitive information, so review and redact them before sharing.

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

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.