Puppeteer screenshot timeout: increase the capture timeout safely
A Puppeteer “screenshot timeout” may come from navigation, a readiness wait, or an outer job limit. Find the failing operation and set its timeout safely.
A Puppeteer “screenshot timeout” usually means that navigation, a readiness wait, a test runner, or an outer job limit timed out before the image was saved. First find the timed-out call in the error stack. Set the timeout on that operation, wait for the page state your capture needs, and then call page.screenshot().
Puppeteer’s documented Page.screenshot() method and ScreenshotOptions do not include a screenshot-specific timeout option. Increasing a navigation or wait timeout does not set a timeout for the screenshot call itself. [Page.screenshot(), ScreenshotOptions]
1. Identify which operation timed out
Read the error message and stack trace to find the promise that rejected. A screenshot workflow often contains several operations, each with a different timeout control.
| Failing operation | What to change |
|---|---|
page.goto(), page.reload(), page.setContent(), or page.waitForNavigation() |
Set that call’s timeout, or adjust the page’s default navigation timeout. |
page.waitForSelector() or another explicit wait |
Set the wait’s timeout, or adjust the default timeout used for waits. |
page.screenshot() |
The reviewed screenshot API does not document a timeout option. Investigate the browser, page, capture settings, and any wrapper or job timeout. |
| Test runner, queue, serverless function, or third-party wrapper | Check that tool’s own timeout and cancellation settings. They are outside Puppeteer’s Page timeout controls. |
Puppeteer’s navigation default applies to navigation methods such as goto() and waitForNavigation(); its documented list does not include screenshot(). [setDefaultNavigationTimeout()]
2. Set a timeout on the operation that is slow
For a slow navigation, give the navigation call a larger, explicit budget. Choose a waitUntil condition that matches what the page needs; the example uses domcontentloaded, which does not wait for every image, font, or later network request.
const url = 'https://example.com';
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: 60_000,
});
await page.screenshot({ path: 'page.png' });
page.setDefaultNavigationTimeout(milliseconds) is useful when several navigations in the same page need the same budget. It covers navigation methods, not screenshot capture. Prefer a per-call timeout when only one navigation is unusually slow, so the limit is visible at the point of use.
page.setDefaultNavigationTimeout(60_000);
await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
});
await page.screenshot({ path: 'page.png' });
For waits that are not navigation, set a timeout on the individual wait or use page.setDefaultTimeout(). Puppeteer’s wait options document a 30,000 ms default; check the API for your installed version. [WaitForOptions]
await page.waitForSelector('[data-ready="true"]', {
timeout: 60_000,
});
await page.screenshot({ path: 'page.png' });
Or set the default for page operations that use the default timeout:
page.setDefaultTimeout(60_000);
await page.waitForSelector('[data-ready="true"]');
await page.screenshot({ path: 'page.png' });
A readiness selector is only useful if the site reliably adds it when the content you need is ready. For an application-specific signal, wait for that signal instead of guessing from elapsed time. Puppeteer’s screenshot guide demonstrates navigation before a page screenshot and a selector wait before an element screenshot. [Puppeteer screenshot guide]
3. Use the right readiness condition
A longer timeout only gives an operation more time; it does not make the page ready or guarantee a useful capture. Pick the readiness condition based on the content:
domcontentloaded: the initial document has been parsed. Use it when the visible content you need is available without waiting for every resource.load: wait for the load event when the capture depends on resources that participate in that event.networkidleor a site-specific readiness signal: use only when it reflects the page’s actual needs. Pages with long-lived requests or frequent background activity may never become network-idle.waitForSelector(): wait for a particular element to appear, become visible, or meet the state your capture requires.- A deliberate delay: reserve this for pages with a known, fixed rendering delay. A fixed sleep can waste time on fast loads and still be too short on slow ones.
These conditions are not interchangeable. For example, a document can be parsed while client-side data is still loading. Conversely, waiting for all network activity to stop can be a poor fit for a page that continuously polls. Use the condition that represents the screenshot’s required content.
4. Complete runnable example
This Node.js script launches Chromium, navigates with an explicit timeout, waits for a page-specific readiness selector, writes a PNG, and closes the browser even if a step fails. Install Puppeteer with npm install puppeteer, save this as screenshot.js, and run node screenshot.js. Replace the URL and selector with ones appropriate for your page.
const puppeteer = require('puppeteer');
async function main() {
const url = 'https://example.com';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
page.setDefaultNavigationTimeout(60_000);
await page.goto(url, {
waitUntil: 'domcontentloaded',
});
await page.waitForSelector('body', {
visible: true,
timeout: 30_000,
});
await page.screenshot({
path: 'page.png',
fullPage: true,
});
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error('Screenshot workflow failed:', error);
process.exitCode = 1;
});
The example uses body as a generic selector to keep it runnable for a normal document; it is not a guarantee that an application has finished rendering. Replace it with a meaningful ready marker when available. A full-page capture can also take more resources on a very long page than a viewport capture.
5. Increase timeouts without hiding failures
- Start with the stack trace. Record which call rejected and the configured timeout.
- Adjust only that step. Use a per-call value when possible; use a page default when several operations share the same legitimate need.
- Set a realistic ceiling. A longer wait can help with slow pages, but can also make a broken or unreachable page occupy a worker longer.
- Keep an outer job limit. If a workflow runs in a test runner, queue, or serverless function, make sure its overall limit allows the Puppeteer step to finish and that it can cancel stuck work.
- Log elapsed time by step. Separate navigation, readiness wait, and screenshot duration so the next timeout identifies the bottleneck.
Puppeteer documents 0 as disabling a wait timeout. Avoid an unbounded wait unless another reliable cancellation mechanism or overall job limit is in place. [WaitForOptions]
6. If the screenshot call itself stalls
If the error stack points to page.screenshot(), changing setDefaultTimeout() or setDefaultNavigationTimeout() is not a documented way to time-limit or extend that call. Puppeteer’s current screenshot method and options references do not list a screenshot timeout field. Check these areas:
- Browser health: confirm Chromium is still running and the page has not crashed or disconnected.
- Capture dimensions: inspect unusually tall pages and full-page capture settings; try a viewport screenshot to isolate page-size pressure.
- Capture options: reduce the capture to the needed viewport or element while diagnosing, then reintroduce options one at a time.
- Wrapper and job limits: inspect the test runner, HTTP request, queue worker, or hosting platform’s timeout separately.
- Version: confirm the installed Puppeteer version and consult its matching API docs. The current docs may not describe an older release or third-party wrapper.
- Reproduction: capture a simple page with minimal options. If that works, add the real URL, full-page mode, and other settings incrementally.
The reviewed official documentation does not establish a universal remedy for a screenshot promise that itself hangs. Treat it as a browser, page, or surrounding-system problem until the stack trace and a minimal reproduction narrow it down.
7. Troubleshooting common timeout errors
| Symptom | Likely cause | What to try |
|---|---|---|
Navigation timeout of ... ms exceeded |
The navigation method did not meet its chosen completion condition within its timeout. | Set a larger timeout on goto() or adjust the navigation default. Reconsider whether waitUntil is stricter than the capture requires. |
Waiting for selector ... failed |
The selector never appeared, was misspelled, or the page did not reach the expected state in time. | Verify the selector against the rendered page, check whether it is inside a frame, and confirm the app can reach that state. Increase the wait timeout only if the state legitimately takes longer. |
| Navigation succeeds, but screenshot content is missing | The navigation condition completed before client-side rendering or the desired content was ready. | Wait for the relevant selector or application-ready signal before capture. |
page.screenshot() appears to hang |
Could involve browser health, very large capture dimensions, capture options, or an outer wrapper limit. | Inspect the browser and wrapper, try a viewport capture, simplify options, and reproduce with a small page. There is no documented screenshot timeout option in the cited API. |
| Timeout occurs after Puppeteer reports success | A test runner, request handler, or job supervisor may have its own deadline. | Check that system’s timeout and cancellation behavior; Puppeteer page defaults do not control it. |
| Timeout changes appear ineffective | The change may target a different operation, a different page, or a wrapper-owned setting. | Log the exact failing call and verify the timeout is configured before that operation on the same page instance. |
8. Performance, reliability, and cost
A larger timeout does not make a capture faster; it raises the maximum time a slow operation can occupy a browser or worker. That can reduce throughput when many pages run concurrently. Use a timeout that accommodates expected variation, then let a separate job limit stop work that exceeds the full task budget.
For reliability, use explicit readiness conditions tied to the content you need, close the browser in a finally block, and record which step failed. Avoid setting every timeout to an unlimited value: a missing selector, unreachable site, or page that never reaches network idle can otherwise retain resources indefinitely.
For cost, account for the runtime and concurrency limits of wherever Chromium runs. The dossier provides no benchmark or per-capture cost figure, so there is no defensible universal timeout value or cost estimate. Measure navigation, readiness, and capture durations in your own workload.
9. Or skip the browser setup
If you need screenshots without maintaining a Puppeteer browser workflow, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns an image or PDF. The API accepts parameters used by other screenshot APIs, which can make switching easier. See 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}`);
const image = Buffer.from(await res.arrayBuffer());
await require('node:fs/promises').writeFile('shot.webp', image);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
10. Frequently asked questions
Does page.screenshot() accept a timeout?
The current Puppeteer screenshot method and options references do not document a screenshot-specific timeout. Configure navigation and readiness waits separately.
Should I use a page-wide default or a per-call timeout?
Use a per-call value when one operation needs more time. Use a page default when a group of similar operations shares the same timing requirement.
Can I set the timeout to zero?
Puppeteer’s wait options document zero as disabling the wait timeout. Do so only if another mechanism can reliably cancel the work.
Which Puppeteer version do these details apply to?
The cited references are the current official documentation surfaced for this guide. Check the documentation matching your installed version, especially when using older Puppeteer releases or a wrapper.


