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.

Puppeteer’s “Navigation Timeout of 30000 ms Exceeded” error means the navigation did not satisfy its configured waitUntil condition before the timeout—30,000 milliseconds by default. It does not, on its own, tell you whether the page is slow, an external resource is hanging, or the chosen readiness condition is too strict. Start by matching the wait condition to what your code actually needs; increase the timeout only if that work is legitimately expected to take longer.

What the 30-second navigation timeout means

Puppeteer waits for a navigation lifecycle condition before treating a navigation-related operation as complete. The default maximum wait is 30,000 milliseconds. The default waitUntil condition is load; if you provide an array of lifecycle conditions, all of them must occur for the wait to succeed. The error means that the selected condition did not complete in time. It does not identify the underlying cause.

The operation may be page.goto(), page.reload(), page.setContent(), page.waitForNavigation(), or another method affected by the page’s default navigation timeout. Puppeteer’s current Page API reference describes that setting as the maximum navigation time in milliseconds. Check the operation named in the stack trace and the options passed to it before changing a timeout globally.

Timeout is not the same as an HTTP error

A navigation timeout is about the wait condition not completing before the deadline. It is a separate issue from the HTTP status returned for a page. Current Page API documentation says headless shell navigation does not throw merely because it receives a valid status such as 404 or 500; inspect the navigation response status separately. A 404 response can arrive promptly, while a page that returns a successful status can still fail to meet a lifecycle wait condition.

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

Diagnose the specific operation before changing settings

  1. Identify the operation. Find the call named in the error or stack trace: for example, goto, setContent, or waitForNavigation. Record the URL or content being loaded and any timeout or waitUntil options set at the call site.
  2. Log what the navigation returned. Capture the response status when one is available, the response URL, the page’s final URL, and the chosen lifecycle condition. A navigation can redirect, and the response may be null in cases where there is no standard response to inspect, so guard against that.
  3. Compare local and deployed runs. If the same operation succeeds locally but fails in a server or container, check whether the runtime can reach the same hosts. DNS, TLS, proxy, firewall, and outbound-network differences are useful leads; the timeout alone does not prove which one is responsible.
  4. Look for work that is not essential. Third-party scripts, fonts, analytics, ads, or API requests may delay the condition you selected. Determine whether your task requires them, whether they are reachable in the deployed environment, and whether the condition is waiting for more than the task needs.

A small amount of context in your logs makes the next choice much safer than simply disabling the timeout. For example, log the URL, the response status if present, and the wait condition alongside the error. Do not treat the status as proof that the rendered page contains the data your script needs; use a readiness signal for that.

Choose a readiness condition that matches the job

Use domcontentloaded when the initial document is enough

If you only need the initial DOM—for example, to inspect markup that is available as soon as the document is parsed—domcontentloaded may be enough. It avoids waiting for the full load condition, which can be delayed by resources that are not relevant to your task. It is not a guarantee that a client-side application has finished rendering its data or that images are ready for a screenshot.

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

console.log({
  requestedUrl: url,
  responseUrl: response?.url() ?? null,
  status: response?.status() ?? null,
  finalUrl: page.url(),
  waitUntil: 'domcontentloaded',
});

This example uses a 60-second per-call limit as an illustration of a bounded increase, not as a universal recommendation. Choose a deadline appropriate for your own workload.

Wait for the application signal when the page is dynamic

For a client-rendered page, navigation completing does not necessarily mean the content you need is ready. Navigate with a reasonable lifecycle condition, then wait for a specific element or application-ready marker. The selector should indicate the data or state your next operation depends on, not merely an element that appears before the work is finished.

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
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#report-ready', { timeout: 15_000 });

const report = await page.$eval('#report-ready', element => element.textContent);

Give the selector wait its own finite timeout so that a missing or renamed marker produces a bounded failure rather than leaving the task stuck. If you are capturing a screenshot or PDF, identify which assets must actually be present and wait for those assets or a page-specific ready signal rather than assuming all network activity will stop.

Use load only when the full load event matters

load is Puppeteer’s default lifecycle condition. Keep it when your task genuinely depends on the page reaching that condition. If it is not completing, inspect the resources the page is trying to load before relaxing the condition. Avoid combining lifecycle events as if an array meant “any one is sufficient”: Puppeteer documents that every event in the array must fire.

Increase the timeout when a slow navigation is expected

If the page needs more time for a legitimate reason and you still need the same lifecycle condition, increase the limit. A per-call timeout changes one operation; page.setDefaultNavigationTimeout() changes the default for navigation-related methods on that page, including back, forward, goto, reload, setContent, and waitForNavigation.

Set a limit for one navigation

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

This keeps the change local to the call. Prefer it when only one destination or operation is slower than usual.

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

Set the page’s default navigation limit

page.setDefaultNavigationTimeout(60_000);
await page.goto(url, { waitUntil: 'load' });

Use a page-wide default only when the longer limit is appropriate for the navigation operations that share that page. It is not the same as changing every kind of wait timeout, and it does not make a page load faster. If a page is consistently slow because a resource is unreachable, a larger limit may only make failures take longer to surface.

Use timeout: 0 only with another deadline

Puppeteer’s WaitForOptions reference says that passing 0 disables the wait timeout. That removes Puppeteer’s bound for the relevant wait; it does not fix a stalled navigation. Do this only for a controlled operation with an independent abort signal or application-level deadline, so a broken resource cannot hold a worker indefinitely. In production automation, a finite limit plus explicit error handling is usually easier to recover from than an unbounded wait.

Fix click-triggered navigation races

A click that starts navigation can race with a separately awaited waitForNavigation(). If the click triggers navigation before the wait has been registered, the wait may miss the event. Start both operations together with Promise.all, using the documented pattern:

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

console.log({
  status: response?.status() ?? null,
  finalUrl: page.url(),
});

This coordinates the wait and click; it does not guarantee that the destination application has finished its own asynchronous rendering. If the next step needs a rendered result, wait for its selector or ready signal after navigation as well.

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

Investigate external resources when setContent() or PDF output hangs

page.setContent() can wait for a lifecycle condition too, so supplied HTML with external dependencies can produce a navigation timeout even though the content is not loaded with page.goto(). In Puppeteer GitHub issue #12077, opened on March 13, 2024, a report involving Puppeteer 21.9.0 and Node 16.20.0 on Linux described page.setContent(html, {waitUntil: 'domcontentloaded'}) followed by page.pdf(); the reporter said removing external resources from the HTML allowed PDF generation. That report is an example of a possible cause, not proof that external resources explain every similar timeout.

For your own HTML, inspect script, stylesheet, font, and image URLs that point outside the document. Confirm the runtime can reach them and decide whether the output actually needs them. If the document can be rendered without a dependency, remove or inline it; if it is required, wait for the particular content you need and retain a finite deadline. A PDF failure after setContent() should be diagnosed at the wait that timed out rather than attributed automatically to PDF generation.

Troubleshoot by symptom

Symptom Likely area to inspect Next action
goto() times out, but the page is usable in a browser The selected waitUntil condition or resources that remain active. Try the least strict condition that meets the task, then wait for a required selector or app-ready signal.
It works locally but times out in CI or a container Differences in DNS, TLS, proxy, firewall, or outbound network access. Compare whether the deployed runtime can reach the page and its external dependencies.
setContent() hangs when HTML includes remote assets External scripts, stylesheets, fonts, images, or other requests. Test whether the output needs each dependency; remove unnecessary resources or wait for the required result explicitly.
A click succeeds but the following navigation wait times out A race between the click and a separately started waitForNavigation(). Register both through Promise.all as shown above.
The error persists after increasing the limit The operation may be waiting for the wrong condition, or a required resource may never complete. Re-check the lifecycle condition and resource reachability instead of continuing to raise the limit.
A response has status 404 or 500 HTTP status handling, not necessarily a navigation timeout. Inspect the response status separately and handle the returned page according to your task.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Navigation waits affect throughput: a longer deadline gives a slow page more time, but a stalled request can also keep a worker occupied longer before it fails. A shorter, task-appropriate lifecycle wait followed by a targeted selector wait can avoid waiting on unrelated work. Keep deadlines finite, log the operation and response context, and handle timeouts as expected failures if your automation runs across many URLs.

For screenshot or PDF jobs, distinguish a browser navigation problem from the capture requirement. A page may be navigable before all screenshot-critical assets are ready, while waiting for every network request may be stricter than needed. Define the visual or document state you require and coordinate your waits around that state. Do not interpret a timeout as evidence that a destination is offline, nor interpret a longer timeout as evidence that a result is complete.

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

Or skip the browser setup

If your goal is simply to capture a website rather than control Puppeteer itself, ScreenshotNeo provides a screenshot API and MCP server. It does not change Puppeteer’s timeout behavior or diagnose your Puppeteer script; it is an alternative when you want the screenshot output without managing a browser capture flow. Its clean-shot steps can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets, and each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and whether the request was billed. AI agents can use its MCP server tools, including take_screenshot, get_page_info, and capture_pdf.

Make one GET request for a screenshot; replace the example URL with the page you want and supply your API key. See the ScreenshotNeo API documentation for request options 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

ScreenshotNeo’s Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Sign up free for 1,000 screenshots a month—no card required.

Frequently Asked Questions

Does this error mean the website is down?

No. The timeout only establishes that Puppeteer did not observe the selected lifecycle condition before the configured deadline. Check the response and the resources your page needs to distinguish a slow or blocked dependency from an unavailable destination.

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

Should I always replace load with networkidle?

No. Choose a condition based on the task. If your requirement is a particular rendered result, a page-specific selector or ready signal is more direct than waiting for general network activity to stop.

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.