What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
“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.
#1 Best Overall
A safe fix sequence
- Log the operation and URL. Record the requested URL, final URL, response status (when available), and the exact
waitUntilvalue. This distinguishes a navigation wait from a later selector or PDF wait. - Choose the least strict readiness signal that satisfies the job. Use
domcontentloadedwhen 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. - 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.
- Increase the timeout for predictably slow pages. Prefer a bounded per-call value or a page default such as 60 seconds.
- 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. - Coordinate click-triggered navigation. Start
waitForNavigation()before the click in aPromise.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.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsRank #2
- 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.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #3
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.
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
- 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
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.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWhen 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.
Best Value
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.
Recommended Free Tools
Quick Recap
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.




