ScreenshotNeo

BlogHow-to

Common Puppeteer Errors and How to Fix Them

Fix Puppeteer install, launch, navigation, and timeout errors by tracing each failure to its stage, environment, and browser version.

By the ScreenshotNeo team4 October 20268 min read

Puppeteer errors are easiest to fix when you identify which stage failed: browser installation, browser launch, navigation, or page interaction. Check the browser version Puppeteer expects, confirm the browser can run in your environment, and use the specific error and diagnostic output to choose a fix. A timeout alone does not identify the cause.

1. Identify the failure stage

Start with the first failing operation and its complete error message. Avoid applying launch flags or increasing every timeout before checking what failed.

Stage Typical symptom First checks
Install Could not find expected browser locally Browser download, cache path, blocked package install scripts
Launch Failed to launch chrome; missing shared library; No usable sandbox! Browser executable, system libraries, sandbox, writable directories
Navigate Navigation timeout or page.goto failure URL, network reachability, SSL, response, navigation wait condition
Interact waitForSelector timeout Selector, page state, iframe, visibility and wait condition

Record the Puppeteer version, Node.js version, operating system or container image, browser path, and whether the same code runs locally. This context helps distinguish a code issue from a deployment or compatibility issue.

2. Fix browser installation and version errors

Could not find expected browser locally

Puppeteer downloads a browser for its release and looks for it in its configured browser cache. Since Puppeteer v19, the default cache is under ~/.cache/puppeteer. A common cause is that installation and runtime use different home directories or cache locations. Another is a package manager or deployment environment that blocked install scripts.

  1. Check whether the install step downloaded a browser and whether the runtime user can read that cache.
  2. Use the same cache configuration during installation and at runtime. If you change the configured cache directory, reinstall the browser afterward.
  3. If install scripts were blocked, install the browser explicitly with npx puppeteer browsers install. Yarn, pnpm, and Bun users can use the corresponding command documented in the Puppeteer troubleshooting guide.
  4. In CI or a container, make browser installation an explicit build step and preserve the downloaded browser in the runtime image.

Consult the official troubleshooting guide for cache configuration details and manual browser installation commands.

Browser and Puppeteer versions do not match

Puppeteer releases are paired with particular browser releases because the automation protocols can change. Puppeteer’s FAQ explains: “Every Puppeteer release is tightly bundled with a specific browser release to ensure compatibility with the implementation of the underlying protocols, the Chrome DevTools Protocol and WebDriver BiDi.” Check the supported browsers table for the project’s exact release before switching to a system-installed Chrome. Puppeteer v20 and later uses Chrome for Testing; older releases used Chromium.

Also confirm that your Node.js version meets the current requirements for your Puppeteer release. The system requirements page lists supported platforms and runtime requirements; check it rather than copying an old version number into a new project.

3. Fix browser launch failures

Missing shared libraries on Linux

A browser executable can exist and still fail to start because the image lacks system libraries. Inspect the browser’s dependencies with ldd on the executable and compare missing dependencies with the platform-specific requirements linked from Puppeteer’s system requirements. Install the packages appropriate to the distribution and base image you actually deploy; package names differ across Linux distributions.

No usable sandbox

Check the host’s sandbox configuration and distribution restrictions. Puppeteer strongly discourages disabling Chrome’s sandbox. Treat --no-sandbox as an exceptional choice only when the content and runtime are fully trusted and you understand the isolation tradeoff. Ubuntu 23.10 and later can also impose AppArmor user-namespace restrictions that affect downloaded Chrome for Testing. See Puppeteer’s sandbox troubleshooting notes before changing launch arguments.

Crashpad database error or read-only container

Chrome writes profile, configuration, and cache data while starting. In a read-only container, provide writable locations and ensure the process user owns them. Puppeteer documents writable /tmp locations for XDG configuration and cache, plus an explicit writable userDataDir. For example:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  userDataDir: '/tmp/puppeteer-profile',
  env: {
    ...process.env,
    XDG_CONFIG_HOME: '/tmp/puppeteer-config',
    XDG_CACHE_HOME: '/tmp/puppeteer-cache',
  },
});

This example assumes the runtime can create and write to those paths. Adapt the paths to the container and user permissions. See the official deployment troubleshooting guidance for the current recommendations.

4. Diagnose navigation and timeout errors

What TimeoutError tells you

Puppeteer’s TimeoutError means an operation exceeded its time limit; it does not say why. It can occur during launch, selector waits, or navigation. Diagnose the operation that timed out before increasing its limit.

Use a navigation wait condition that matches the page

page.goto() can reject for an invalid URL, SSL failure, unreachable server, timeout, failed main resource, or a URL rejected by blocklist or allowlist rules. A valid HTTP response such as 404 or 500 does not by itself make navigation throw in headless shell: inspect the returned response status. The Frame.goto API reference also documents special behavior for about:blank and same-URL hash changes.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  page.setDefaultNavigationTimeout(45_000);

  const response = await page.goto('https://example.com', {
    waitUntil: 'domcontentloaded',
    timeout: 45_000,
  });

  if (!response) {
    throw new Error('Navigation returned no main-resource response');
  }
  console.log('HTTP status:', response.status());
  console.log('Final URL:', page.url());
} finally {
  await browser.close();
}

domcontentloaded is often a more practical starting point than waiting for every network connection to close, especially on pages that keep analytics or live connections open. Choose a condition based on what the task needs; it does not guarantee that client-rendered content or a particular element is ready.

Selector wait timeouts

Before extending a selector timeout, verify the selector in the actual rendered DOM, that navigation reached the expected page, and that the element is in the main frame rather than an iframe. Check whether the page requires a click, authentication, or client-side rendering before the element appears.

const selector = '[data-testid="result"]';
await page.waitForSelector(selector, { timeout: 15_000 });
const result = await page.$eval(selector, element => element.textContent?.trim());
console.log(result);

If the selector belongs to an iframe, locate that frame and wait there. If it is hidden or replaced during rendering, wait for the state your task needs and avoid treating mere DOM presence as proof that an interaction will succeed.

net::ERR_BLOCKED_BY_CLIENT on remote HTTP pages

Puppeteer’s troubleshooting guide describes a Chrome for Testing HTTPS warning behavior that can produce this error for remote HTTP navigation. Confirm that the browser is showing the described warning interstitial before applying its documented workaround. The guide notes that local HTTP hosts do not trigger this specific case. Do not treat every ERR_BLOCKED_BY_CLIENT as the same issue.

5. Capture useful diagnostics

When the likely cause is still unclear, collect browser output instead of guessing. Set dumpio: true to forward browser process output to Node’s standard streams. For asynchronous protocol problems, Puppeteer’s debugging guide describes protocol logging with NODE_DEBUG and inspecting browser.debugInfo.pendingProtocolErrors.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ dumpio: true });
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
} finally {
  await browser.close();
}

Capture the full stack trace and the browser’s standard error output, along with versions and runtime details. Logs may contain URLs, request information, or page data, so review and redact sensitive details before sharing them.

6. Common errors and fixes at a glance

Error Likely cause to check Practical fix
Could not find expected browser locally Browser was not installed, install scripts were blocked, or runtime cache differs Install with the Puppeteer browser command; align install/runtime cache configuration
Failed to launch chrome Missing Linux libraries, incompatible executable, or runtime setup Check ldd, platform dependencies, and supported browser mapping
No usable sandbox! Host or distribution sandbox configuration Diagnose sandbox setup; avoid disabling it unless the content is fully trusted
chrome_crashpad_handler: --database is required Chrome cannot write required profile/config/cache data Provide writable, correctly owned temporary paths and a writable profile directory
TimeoutError The specific launch, navigation, or wait operation exceeded its timeout Identify the operation and its condition; investigate state and reachability before increasing the limit
page.goto rejects Invalid URL, SSL, network, failed main resource, timeout, or URL rule Check URL and connectivity, inspect exception and response, review navigation options
ERR_BLOCKED_BY_CLIENT May be the documented Chrome for Testing HTTP warning, or another blocking condition Verify the actual page/interstitial, then follow the matching troubleshooting guidance

7. Performance, reliability, and cost considerations

Browser automation consumes CPU and memory, and each launched browser has startup overhead. Reuse a browser process for multiple pages when the workload and isolation requirements allow it, close pages and browsers in cleanup paths, and use explicit navigation and selector waits rather than long fixed sleeps. In CI, install the browser and dependencies in the image and keep Puppeteer and browser versions aligned so runs do not depend on a developer machine’s system Chrome.

Choose timeouts based on the operation and environment. A longer timeout can accommodate a slow but valid page; it cannot fix an absent browser, missing library, blocked URL, or selector that never appears. For reliability, log the failed stage, versions, URL where appropriate, and browser diagnostics. Puppeteer is software you operate, so budget for browser compute, image/container storage, dependency updates, and the engineering time needed to maintain the runtime.

8. Or skip the browser setup

If the goal is to get a website screenshot rather than operate Chrome, ScreenshotNeo provides a screenshot API and MCP server. Its API accepts a URL in one GET request and returns an image or PDF. See the ScreenshotNeo API documentation for parameters.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Replace YOUR_API_KEY with your key. The Python example requires the requests package; the Node.js example uses the built-in fetch API and Bun’s file writer. With Node.js, save the response body using your preferred file-writing method.

  • Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Response headers indicate the page verdict and billing status.
  • An MCP server gives AI agents screenshot tools, including take_screenshot, get_page_info, and capture_pdf.
  • The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo and get 1,000 screenshots a month free, with no card.

9. FAQ

Does a 404 response mean page.goto() failed?

No. A valid HTTP response can resolve normally even when its status is 404 or 500. Inspect the response status and decide how your application should handle it.

Should I use my system Chrome?

Only after checking that its version is supported by your installed Puppeteer release. The supported browser mapping is the reliable reference.

Is increasing the timeout a fix?

Only when the operation is valid and needs more time in that environment. First determine whether the delay comes from navigation, page state, launch, or a missing prerequisite.

Where should I report a problem?

Use the Puppeteer project’s support channels after collecting a small reproduction, version information, the complete error, and relevant diagnostics. Remove secrets and private page data from logs.