ScreenshotNeo

BlogHow-to

How to Handle Puppeteer Browser Timeout Errors

Find which Puppeteer operation timed out, check what it was waiting for, then choose a targeted fix for navigation, selectors, or browser startup.

By the ScreenshotNeo team4 October 20268 min read

A Puppeteer timeout means a particular operation did not finish before its deadline. It does not tell you why. First identify the rejected call and its expected completion condition; then fix that condition or adjust the timeout at the narrowest appropriate scope.

This guide covers browser startup, navigation, selectors and other waits, plus debugging and deployment issues. The API defaults below follow the current Puppeteer 25.x documentation, which lists 30,000 ms for common wait operations and browser launch. Check the documentation for your installed version if behavior differs.

1. Find the operation that timed out

Read the full error and stack trace. Record the method, URL or selector, timeout value, and whether the failure happened locally or in a deployed runtime. Puppeteer’s TimeoutError can come from operations such as page.waitForSelector() or puppeteer.launch(); it is not synonymous with a navigation timeout. See the TimeoutError API reference.

Where it fails What to inspect first
puppeteer.launch() Browser download, executable path, permissions, platform dependencies, and runtime resources.
page.goto() or another navigation method URL, redirects, navigation lifecycle condition, and whether navigation actually occurs.
waitForSelector() or a locator action Selector spelling, frame context, visibility/action preconditions, and whether the page reaches the expected state.
waitForFunction(), network idle, or another explicit wait The exact predicate or activity threshold; confirm it can become true on this page.

Keep response status errors separate from timeouts. A navigation can complete with an HTTP error status; inspect the response as well as the wait result when status matters. Puppeteer’s Page API reference also documents a headless-shell caveat for navigation responses with valid HTTP status codes.

2. Set the timeout at the right scope

Common wait options use a 30,000 ms default. A per-operation timeout overrides it. For broader policies, page.setDefaultTimeout(ms) applies to page waits generally, while page.setDefaultNavigationTimeout(ms) applies to goto, reload, setContent, waitForNavigation, goBack, and goForward. The navigation setting takes precedence for those navigation methods. Values are in milliseconds. See WaitForOptions and setDefaultNavigationTimeout.

const { launch } = require('puppeteer');

(async () => {
  const browser = await launch();
  try {
    const page = await browser.newPage();

    // Per-navigation timeout and lifecycle condition.
    await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded',
      timeout: 45_000,
    });

    // Defaults for later waits on this page.
    page.setDefaultTimeout(20_000);
    page.setDefaultNavigationTimeout(45_000);

    // A selector with its own narrower timeout.
    await page.waitForSelector('main article', { timeout: 10_000 });
    console.log('Target content is present');
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error.name, error.message);
  process.exitCode = 1;
});

Use timeout: 0 only when an unbounded wait is an intentional policy and you provide another way to stop the job. It disables the timeout, so an impossible condition can wait indefinitely. Prefer a finite operation-level limit for exceptional slow steps.

3. Match navigation waits to the page

page.goto() defaults to waiting for the load lifecycle event. Its waitUntil option can use load, domcontentloaded, networkidle0, or networkidle2 (or an array of lifecycle events). Select the least strict condition that still makes the next action safe. For a client-rendered app, navigation completion may not mean the required content is ready; wait for a page-specific selector or predicate after navigation. See the GoToOptions reference.

await page.goto('https://example.com/dashboard', {
  waitUntil: 'domcontentloaded',
  timeout: 30_000,
});

// Wait for the application state the next step actually needs.
await page.waitForSelector('[data-state="ready"]', { timeout: 15_000 });

Use networkidle0 or networkidle2 only if network quiet is a meaningful signal for your page. Long-lived requests or recurring background traffic can make network-idle unsuitable. page.waitForNetworkIdle() waits for network idle and at least the configured idle period; its options document a 500 ms default idle time. See waitForNetworkIdle.

4. Diagnose selector and locator timeouts

A selector wait can expire because the selector is wrong, the page is in a different state than expected, or the target is inside a frame. During diagnosis, inspect the DOM and verify the selector in the correct document or frame. Check whether the target must be visible or actionable, rather than merely present.

Locators wait for element presence and action preconditions and inherit the page timeout by default; a locator can also have its own timeout. This helps synchronize actions but cannot make an incorrect selector or impossible state succeed. See the Page interactions guide.

// If the element lives in an iframe, query the frame rather than the top page.
const frame = page.frames().find(f => f.url().includes('/embedded/'));
if (!frame) throw new Error('Expected embedded frame was not found');
await frame.waitForSelector('button.submit', { timeout: 10_000 });

// For a page-level application condition, state the condition explicitly.
await page.waitForFunction(
  () => document.querySelector('main')?.dataset.ready === 'true',
  { timeout: 15_000 },
);

5. Handle browser launch timeouts separately

LaunchOptions.timeout controls how long Puppeteer waits for the browser to start; its documented default is 30,000 ms. If launch times out, first establish that the expected browser was installed and that the process can access its browser cache or configured executable. Check install scripts, platform dependencies, executable configuration, filesystem permissions, and available runtime resources before increasing the launch timeout. The Puppeteer troubleshooting guide covers missing browser downloads, blocked install scripts, sandbox and permission concerns, dependencies, and deployment environments.

const browser = await require('puppeteer').launch({
  timeout: 60_000, // Use only if startup is legitimately slow.
  // executablePath: '/path/to/chrome', // Set only when your deployment requires it.
});

Puppeteer says it is only guaranteed to work with its bundled browser; using a different executable is at the user’s risk. Avoid treating --no-sandbox as a routine timeout fix. The troubleshooting guide discourages running without a sandbox and recommends configuring one where possible.

The guide documents a specific Google Cloud Run case: CPU can be disabled after an HTTP response is written, so launching Puppeteer in background work after responding can appear very slow. Depending on the service design, keep CPU available for that work or launch before responding. This scenario is specific to that runtime and should not be assumed to explain other environments.

6. Inspect browser behavior and collect useful evidence

When the failing condition is still unclear, make the browser easier to observe. Puppeteer’s debugging guide describes headful mode and slowMo for inspecting interaction behavior. Capture console messages and relevant request or response events so you can see whether the page is navigating, loading an error, or failing before the expected state. See the debugging guide.

const browser = await require('puppeteer').launch({
  headless: false,
  slowMo: 100,
});
const page = await browser.newPage();
page.on('console', message => console.log('PAGE:', message.type(), message.text()));
page.on('requestfailed', request => {
  console.log('REQUEST FAILED:', request.url(), request.failure()?.errorText);
});
page.on('response', response => {
  if (response.status() >= 400) {
    console.log('HTTP:', response.status(), response.url());
  }
});

Headful mode and slow motion are diagnostic aids; they do not fix a faulty condition. Remove them from production paths unless they are deliberately part of your workflow.

7. Troubleshooting common Puppeteer timeout errors

Symptom Likely cause Fix
Navigation timeout of 30000 ms exceeded The chosen lifecycle condition did not happen before the deadline, or the URL is stalled. Confirm the URL and navigation behavior. Choose a suitable waitUntil condition, then wait for the required application state separately.
Waiting for selector ... failed Selector mismatch, wrong frame, or target never appears. Inspect the live DOM and frame context; correct the selector or application precondition.
TimeoutError during a locator click The locator’s target or action preconditions are not met in time. Check locator match, visibility and actionability, and page state. Set a per-locator timeout only if the action legitimately needs longer.
Timed out after ... ms while waiting for the browser Browser is missing, inaccessible, incompatible, or slow to start in the runtime. Verify installation, executable access, dependencies, permissions, and resources; then adjust launch timeout if justified.
Network-idle wait never completes The page may maintain requests or background traffic. Use a meaningful selector or predicate if network silence is not a valid readiness condition.
Works locally but times out in deployment Different browser installation, sandbox, permissions, dependency set, or CPU/resource behavior. Compare runtime setup with Puppeteer’s deployment troubleshooting guidance; investigate the specific platform behavior.
Timeout disappears after setting every timeout to zero The underlying condition may still be impossible or stalled. Restore finite limits, locate the blocked step, and give only that operation a suitable budget.

8. Performance, reliability, and cost considerations

  • Timeouts bound wasted work. Finite limits help a job fail predictably when a page cannot reach its target state. Set them according to the operation, not a blanket guess.
  • Relaxing a condition can improve completion time. Waiting for domcontentloaded and then a specific readiness selector may avoid waiting for unrelated resources, while preserving the actual requirement.
  • Retries need a reason and a limit. Retry transient navigation or infrastructure failures only when safe; repeated attempts at a wrong selector or impossible state add latency and resource use.
  • Browser launch is a separate cost. Reusing a browser process can avoid repeated startup work in a long-running worker, but isolate pages and close resources according to your application’s lifecycle.
  • Do not infer success from a longer timeout. Record which operation and condition completed so production diagnostics distinguish slow pages from broken automation.

9. Or skip the browser setup

If the task is to capture a website rather than automate browser behavior, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call endpoint returns an image or PDF, with configuration details in the ScreenshotNeo API documentation.

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)
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 require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Each response reports the page verdict and billing status. Sign up for 1,000 free screenshots a month, with no card required.

10. Frequently asked questions

Does a Puppeteer timeout mean the website is down?

No. It only establishes that an operation did not meet its completion condition in time. Check the method, response status, browser logs, and page state before diagnosing the site.

Should I always use networkidle0 for screenshots?

No. A page can keep network activity open, and network silence may not correspond to visual readiness. Use a page-specific readiness condition when that better represents the result you need.

Can a locator fix a selector timeout?

It can wait for presence and action preconditions more directly, but it cannot correct a selector that does not match or a state the page never reaches.

Which timeout should I increase for a slow browser?

If the rejection occurs during launch(), review the launch timeout and browser runtime. A navigation or selector timeout is controlled by a different operation and scope.