Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
browser automation

How to Fix Puppeteer’s “Navigation Timeout of 30000 ms Exceeded” Error

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

“Navigation Timeout of 30000 ms Exceeded” means Puppeteer waited 30,000 milliseconds for the navigation condition you selected, but that condition never completed. The reliable fix is to match waitUntil to what your task actually needs, remove or isolate slow external resources, and only then raise the timeout. Use timeout: 0 only with your own cancellation deadline.

What the error actually means

Puppeteer applies a 30,000-millisecond default to navigation-related waits. The timeout is a limit on waiting for a lifecycle event, not a statement that the server took exactly 30 seconds to answer. A page can return a response quickly while a script, font, iframe, analytics request, or never-ending connection prevents the chosen condition from firing.

The default waitUntil condition for navigation is load. If you pass an array, every event in that array must occur before the wait succeeds. Puppeteer’s navigation API describes the setting as the maximum navigation time in milliseconds; the wait options allow 0 to disable that timeout.

The same class of wait affects page.goto(), page.reload(), page.goBack(), page.goForward(), page.setContent(), and page.waitForNavigation(). First identify which operation threw, then inspect its lifecycle condition.

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

A safe fix sequence

  1. Log the operation and URL. Record the requested URL, final URL, response status (when available), and the exact waitUntil value. This distinguishes a navigation wait from a later selector or PDF wait.
  2. Choose the least strict readiness signal that satisfies the job. Use domcontentloaded when the initial DOM is enough. For a screenshot or PDF, navigate first and then wait for the selector or application signal that proves the content is ready.
  3. Check external resources. Third-party scripts, fonts, ads, analytics, API calls, and iframes can be slow, blocked, or unavailable in a container. Temporarily remove them or intercept requests to prove whether one is holding up the lifecycle event.
  4. Increase the timeout for predictably slow pages. Prefer a bounded per-call value or a page default such as 60 seconds.
  5. Use an unlimited wait only under a separate deadline. With timeout: 0, a broken resource can hold a worker forever unless your code supplies cancellation and job-level time limits.
  6. Coordinate click-triggered navigation. Start waitForNavigation() before the click in a Promise.all; otherwise the navigation can begin before the listener is attached.

Pick the right waitUntil condition

Condition What it waits for Use it when Typical risk
domcontentloaded The document has been parsed. You need the initial DOM and will perform your own readiness check. Images, styles, fonts, and app data may still be loading.
load The browser’s load lifecycle event. Core page assets must be loaded before continuing. A single slow or failed external asset can delay completion.
networkidle0 No active network connections for the idle window. A page is known to finish all requests and truly become quiet. Polling, analytics, WebSockets, or long requests can prevent idle forever.
networkidle2 No more than two active network connections for the idle window. You need a stronger signal than DOM parsing but the page has some background traffic. Background requests can still make timing unpredictable.

For most scraping, start with domcontentloaded and wait for the exact element or application state you consume. Do not add every lifecycle event “for safety”: an array requires all of them and therefore makes the strictest requirement decisive.

Working navigation patterns

Initial DOM is sufficient

const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();

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

const title = await page.title();
console.log(title);
await browser.close();

This raises the limit to 60 seconds while using a less demanding readiness condition. A longer timeout alone does not fix a lifecycle event that can never occur.

Wait for the application’s own ready signal

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

await page.waitForSelector('#report-ready', {
  visible: true,
  timeout: 15_000,
});

const html = await page.$eval('#report-ready', el => el.outerHTML);

A selector, a known text marker, or a page-specific JavaScript state is usually a better guarantee for screenshots and PDFs than waiting for all network activity to stop.

Set a page-wide navigation default

page.setDefaultNavigationTimeout(60_000);
await page.goto('https://example.com', {waitUntil: 'load'});
await page.reload();

This default applies to navigation-related methods including back, forward, goto, reload, setContent, and waitForNavigation. Keep operation-specific overrides when different pages have materially different budgets.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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

Click and navigation without a race

const [response] = await Promise.all([
  page.waitForNavigation({
    waitUntil: 'domcontentloaded',
    timeout: 60_000,
  }),
  page.click('a.next'),
]);

console.log('final URL:', page.url());
console.log('status:', response && response.status());

Creating the navigation promise and clicking in the same Promise.all prevents the click from winning the race. If the click updates the current document without a real navigation, wait for the resulting selector instead.

Controlled unlimited waits

const controller = new AbortController();
const deadline = setTimeout(() => controller.abort(), 120_000);

try {
  await page.goto('https://example.com', {
    waitUntil: 'domcontentloaded',
    timeout: 0,
    signal: controller.signal,
  });
} finally {
  clearTimeout(deadline);
}

timeout: 0 disables Puppeteer’s wait timeout. Retain an independent deadline, queue timeout, or worker watchdog so an unavailable dependency cannot consume the process indefinitely. If your installed Puppeteer version does not support the shown signal option, enforce the deadline at the job or browser-process level.

Diagnose slow or blocked resources

Capture navigation facts

page.on('requestfailed', request => {
  console.warn('request failed:', request.url(), request.failure());
});

page.on('response', response => {
  if (response.status() >= 400) {
    console.warn('HTTP', response.status(), response.url());
  }
});

const response = await page.goto(url, {
  waitUntil: 'domcontentloaded',
  timeout: 60_000,
});

console.log({
  requested: url,
  final: page.url(),
  status: response && response.status(),
});

A failed request event points to a resource problem; it does not automatically mean the document navigation itself failed. A valid HTTP 404 or 500 response is also separate from a navigation timeout: inspect the response status and decide whether your application should reject it.

Test external HTML and scripts

If page.setContent() times out before a PDF, remove remote scripts, stylesheets, fonts, and images from a minimal reproduction. A documented Puppeteer issue reported that removing external resources allowed PDF generation, while deployed HTML containing external scripts reproduced the timeout. If the stripped document works, self-host required assets, provide reachable URLs from the runtime, or wait for a specific application-ready marker rather than the broadest lifecycle condition.

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

Check deployment parity

  • Verify DNS resolution from the container or CI runner, not only from your laptop.
  • Check outbound firewall, proxy, TLS certificate, and authentication requirements.
  • Confirm that the target does not require an interactive login, consent action, or bot challenge.
  • Compare the browser’s user agent, timezone, and cookies with the environment where the page normally works.
  • Look for polling, WebSockets, service workers, and long-lived requests before choosing a network-idle condition.

Common symptoms and targeted fixes

Symptom Likely cause Fix
goto times out, but the page appears in a manual browser. Different DNS, proxy, TLS, cookies, or bot response in the server environment. Log final URL and status, inspect failed requests, and reproduce inside the same container.
domcontentloaded succeeds but the screenshot is blank. Content is rendered after JavaScript or an API call. Wait for a content selector or app-ready signal, then capture.
networkidle0 never completes. Polling, analytics, a WebSocket, or an intentionally open request. Use DOM parsing plus a selector, or choose a bounded idle condition only when appropriate.
setContent hangs before pdf(). Remote assets in the supplied HTML are slow or unreachable. Inline or self-host essential assets, remove nonessential resources, and wait for a specific element.
Timeout occurs after a link click. Race between the click and waitForNavigation, or the click is not a navigation. Use the documented Promise.all pattern, or wait for the resulting DOM change.
Increasing to 60 seconds only delays failure. The selected condition cannot complete. Change waitUntil, remove the blocking resource, or replace lifecycle waiting with an explicit readiness check.

Timeouts, retries, and reliability

Use separate budgets for navigation, application readiness, and the overall job. For example, allow 60 seconds for navigation, 15 seconds for a required selector, and a larger outer deadline for cleanup and logging. Keep retries limited: retrying a deterministic missing resource only adds load and latency. If you retry transient failures, create a fresh page or browser context when state, service workers, or cookies may be involved, and record the attempt number and final URL.

Do not treat a successful navigation promise as proof that the page is valid. Check the response status, expected hostname, authentication state, and required selector before saving a screenshot or PDF. Conversely, do not classify every non-2xx status as a Puppeteer timeout; HTTP status handling belongs in your application policy.

Or skip the browser setup

For a production screenshot without maintaining Chromium navigation code, ScreenshotNeo accepts one GET request and returns PNG, JPEG, WebP, or PDF. It removes cookie-consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every plan includes the features, with 1,000 screenshots per month free without a card and paid plans starting at $5 for 3,000 shots.

See the parameter reference in the ScreenshotNeo documentation.

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

cURL

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

Python

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)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

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
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

Does the error mean the website is down?

No. It means Puppeteer’s selected lifecycle condition did not complete within its wait budget. The server may have responded while a dependent resource remained pending.

Should I always use networkidle0 for PDFs?

No. Pages with polling, analytics, WebSockets, or long requests may never become idle. Wait for the assets or selector that proves the document is ready.

What is the difference between a navigation timeout and a 500 response?

A timeout concerns Puppeteer’s wait for lifecycle completion. A 500 is an HTTP response your code must inspect and handle separately.

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

When is timeout: 0 appropriate?

Only for a controlled operation with an independent cancellation or job deadline. Otherwise one unresolved request can occupy a worker indefinitely.

Frequently Asked Questions

Can a redirect cause this timeout?

Yes. Log the final URL and each response while testing. A redirect loop or a redirect to an unreachable host can prevent the chosen lifecycle condition from completing.

Why does a selector wait fail after navigation succeeds?

Navigation readiness and application readiness are different. The document can be parsed while JavaScript has not yet rendered the selector; increase the selector’s own timeout or verify that the selector is correct for the returned page.

Do retries fix a timeout caused by a third-party script?

Usually not. First remove, replace, or make that dependency reachable, then retry only failures that are demonstrably transient.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.