ScreenshotNeo

BlogHow-to

Chrome Headless screenshot timeout on slow web pages: how to fix it

Find which screenshot step is timing out, choose a readiness signal that matches the content you need, and fix slow captures in Chrome, Puppeteer, or Playwright.

By the ScreenshotNeo team4 October 20268 min read

First identify which operation timed out: Chrome’s command-line screenshot wait, browser navigation or readiness waiting, or the call to a screenshot API. They have different controls. Increasing a timeout can prevent a premature failure, but it does not ensure that the content you need has rendered.

For a bounded Chrome Headless capture, use --timeout=<milliseconds>. For Puppeteer or Playwright, pick a navigation condition and, when necessary, check for the specific page content your screenshot must include. There is no single wait strategy that fits every site.

1. Identify the operation that timed out

Before changing a timeout, record the exact error and the call that produced it. A timeout can come from several separate waits:

Operation What it is waiting for Where to investigate
Chrome CLI screenshot capture The configured maximum wait before Chrome captures The command, Chrome executable, and --timeout value
Navigation or readiness wait A navigation event, network condition, selector, or other application state The goto or navigation call and its wait condition
Screenshot call or screenshot API The screenshot operation or a service-side request and its configured deadline The screenshot call, API response, and any service timeout information

To narrow it down, collect the library or command you use, the full timeout error, the failing call, the wait condition, whether the page eventually shows the required content, and whether the capture runs locally or in a hosted runtime. Without these details, the title alone does not establish a root cause.

2. Chrome Headless CLI: set a bounded capture wait

Chrome Headless supports --timeout=<milliseconds> with --screenshot. Chrome captures after this maximum wait even if the page is still loading. For example:

chrome --headless --screenshot --timeout=10000 https://example.com/

Here, 10000 is 10 seconds. Adapt the executable name and duration to your environment. The flag sets when Chrome captures; it does not wait for a particular application widget, late-loading image, or other page state to become ready. A longer value may give content more time, but it cannot guarantee that content appears.

See the Chrome Headless command-line reference for the CLI options.

3. Puppeteer: separate navigation from capture

In Puppeteer, navigation and screenshot capture are separate steps. Log them separately so you can tell whether page.goto() or page.screenshot() failed. Choose an intentional waitUntil condition based on the page and the content needed in the image.

import puppeteer from 'puppeteer';

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

  console.log('Starting navigation');
  const response = await page.goto('https://example.com/', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  console.log('Navigation finished', response?.status());

  console.log('Starting screenshot');
  await page.screenshot({ path: 'page.png', fullPage: true });
  console.log('Screenshot saved');
} finally {
  await browser.close();
}

This is runnable as an ES module in a project with Puppeteer installed. The 30-second navigation limit is an example, not a recommended universal value. Puppeteer’s screenshot guide demonstrates navigating with waitUntil: 'networkidle2' before calling page.screenshot(), but a quiet network is not always the right readiness signal. Sites that keep requests open or render content later may need a different condition. See the official Puppeteer screenshots guide.

If the navigation finishes but the required content is still absent, wait for a meaningful page-specific condition before capturing. If navigation finishes and the screenshot call itself fails, investigate that call separately instead of only increasing the navigation timeout.

4. Playwright: choose a navigation state, then verify content

Playwright provides four navigation states: commit, domcontentloaded, load, and networkidle. They signal different milestones:

State What it signals Consider it when
commit The response has been received and the document has started loading You need an early navigation milestone and will check page readiness separately
domcontentloaded The initial document has been parsed The needed content is in the initial document or you will wait for it explicitly
load The page’s load event has fired The page’s required resources are expected to finish by that event
networkidle Network activity has reached the documented idle condition A network quiet period suits the page; persistent requests can make this a poor fit

Playwright discourages using networkidle as a testing readiness check and recommends assertions to determine when a page is ready. For screenshots, apply the same principle: check for the meaningful content the image needs instead of assuming a network event proves it is visible.

import { chromium } from 'playwright';

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

  console.log('Starting navigation');
  await page.goto('https://example.com/', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });

  // Replace this selector with content that must appear in your screenshot.
  await page.locator('main').waitFor({ state: 'visible', timeout: 15000 });

  console.log('Required content is visible; starting screenshot');
  await page.screenshot({ path: 'page.png', fullPage: true });
  console.log('Screenshot saved');
} finally {
  await browser.close();
}

Use a selector that represents the actual content required in the capture. If the page has no stable selector, identify an application-specific assertion or state. Adjust the navigation timeout and the selector wait separately because they cover different operations. See the official Playwright Page API.

5. A practical troubleshooting sequence

  1. Save the exact error. Include its stack trace and identify whether it comes from navigation, a readiness check, screenshot capture, or a remote API request.
  2. Log before and after each awaited operation. For example, log around navigation, selector waits, and screenshot calls. This identifies the step that stalls.
  3. Write down the required screenshot content. Is it enough to capture the initial document, or must a particular app section, image, or client-rendered result be present?
  4. Choose a matching readiness signal. Use a navigation milestone for navigation, and a meaningful content check when the required content can appear after that milestone.
  5. Check what the page does while waiting. Observe whether it eventually renders, remains active with requests, or never reaches the expected state. Treat slowness, persistent activity, blocking, and delayed rendering as possibilities to diagnose, not assumed causes.
  6. Change only the relevant timeout. Raise the navigation limit if navigation is genuinely slow; change a selector wait if the expected content is delayed; configure a screenshot or API deadline only if that is the failing operation.
  7. Repeat with the same URL and runtime. A local browser and a hosted runtime may have different behavior. Record the runtime and compare the exact failing step.

6. Common timeout symptoms and fixes

Symptom Possible explanation What to do
Chrome CLI returns a screenshot, but late content is missing The maximum capture wait elapsed before that content appeared Increase the bounded wait if appropriate, or use browser automation to wait for a page-specific condition
page.goto() times out The chosen navigation condition did not occur within its limit Log the failing URL and condition; select a condition that matches the page and set the navigation timeout deliberately
A network-idle wait hangs or times out The page may keep network activity open or recurring Use another suitable navigation milestone and wait for the content required in the screenshot
Navigation succeeds, but the screenshot lacks app content The app may render the required content after the navigation event Wait for a meaningful selector or application assertion before capturing
The navigation and readiness checks finish, but screenshot capture fails The screenshot operation has its own error or deadline Inspect the screenshot call and its error independently; do not assume a navigation timeout setting controls it
A hosted capture times out while local capture succeeds The two runs may differ in runtime or page behavior Compare logs, URL, browser configuration, and the failing phase in both environments before selecting a fix

These are diagnostic leads, not a diagnosis for a particular URL. The exact error, failing call, wait condition, page behavior, and runtime are needed to establish the cause.

7. Readiness, speed, reliability, and cost

Use a condition that means something for the screenshot

Navigation states tell you about document or network progress; they do not necessarily prove that a specific client-rendered element is ready. A short, relevant content check can be more reliable than waiting for all network activity to stop. Conversely, waiting on a selector that never appears will itself time out, so choose a real signal and make that wait bounded.

Keep waits bounded and diagnose before raising them

Longer limits can accommodate genuinely slow navigation, but they also increase how long a failed capture can occupy a worker. A timeout increase can conceal a readiness condition that never becomes true. Log each phase and use separate, intentional limits for navigation, content readiness, and screenshot or API operations.

Make retries useful

When a capture fails, retain the URL, phase, wait condition, runtime, and error. Retry only when the failure may be transient, and keep each attempt bounded. Repeating the same wait for content that never appears will not make the page ready; use the logs to distinguish a transient delay from a condition or environment mismatch.

Account for capture method costs

With a self-hosted browser, account for the runtime and worker time spent waiting and capturing. With a screenshot API, check that provider’s pricing and billing rules rather than assuming a timeout is free. The timeout settings described above do not specify provider pricing.

8. Or skip the browser setup

If you want an API to handle the browser capture, ScreenshotNeo takes a URL in one GET request and returns a PNG, JPEG, WebP, or PDF. The API supports wait options, including a selector, delay, or network idle. See the ScreenshotNeo API docs for request options.

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}`);

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with the outcome reported in response headers. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Sign up free for 1,000 screenshots a month, with no card required.

9. FAQ

Does a bigger timeout guarantee a complete screenshot?

No. It allows more time for the operation being timed, but the page can still fail to reach the content state you need. Choose and check a readiness signal that corresponds to that content.

Should I always wait for networkidle?

No. Persistent or recurring requests can make network-idle waits unsuitable, and Playwright discourages the state as a testing readiness check. Prefer a meaningful assertion for required page content when possible.

What information is needed to diagnose my exact timeout?

Share the browser library or command, complete timeout error, failing call, wait condition, URL behavior, and whether the run is local or hosted. Those details identify which phase needs attention.