Skip to content
Playwright · error message

Playwright: page.goto: Timeout 30000ms exceeded

RetryableNavigation & network

Last verified

The exact message
TimeoutError: page.goto: Timeout 30000ms exceeded. Call log: - navigating to "https://example.com/", waiting until "load"

What it means

The navigation started, but the page did not reach the requested load state before the navigation timeout. By default page.goto waits for load, which fires only after every image, stylesheet, script and iframe referenced by the page has finished. One slow third-party script, analytics beacon or ad iframe is enough to hold it open.

The last line of the call log tells you which state was awaited. If it says waiting until "load", the HTML probably arrived long ago and a sub-resource is the bottleneck. If the server never responded at all you will also see this error, so check the network tab of a trace.

What is not wrong: a timeout is not a DNS or connection failure; those fail immediately with net:: codes. And the page may be perfectly usable; you are simply waiting for the wrong signal.

What it looks like

// JavaScript
await page.route(/(analytics|doubleclick|hotjar)/, route => route.abort());
await page.goto('https://example.com/', { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();

# Python
page.goto("https://example.com/", wait_until="domcontentloaded")
expect(page.get_by_role("heading", level=1)).to_be_visible()

Why it happens

  • waitUntil is load (the default) or networkidle and a third-party resource never finishes loading.
  • The site streams or long-polls, so the network never goes idle.
  • A bot-protection interstitial or challenge page holds the response.
  • The server is genuinely slow or the job runs through a slow proxy.
  • Resource-heavy pages on an underpowered CI machine.

How to fix it

  1. Open the trace (npx playwright show-trace) and check which requests were still pending at the timeout.
  2. Wait for less: page.goto(url, { waitUntil: 'domcontentloaded' }) or 'commit', then wait for the element you need with expect(locator).toBeVisible().
  3. Avoid networkidle; Playwright's docs discourage it for testing.
  4. Block heavy or irrelevant third-party requests with page.route() returning route.abort().
  5. Only then raise the limit with page.setDefaultNavigationTimeout() or the goto timeout option.

How to stop it recurring

Treat navigation as "the document arrived" and readiness as "the element I need is visible". Waiting on a concrete element is faster and more reliable than any load event. Keep third-party scripts out of automated runs where possible, and record traces on failure so slow requests are visible rather than guessed at.

Official documentation: playwright.dev/docs/api/class-page#page-goto. See all Playwright error messages.

Related errors

Definitions

Quick answers

What does "page.goto: Timeout 30000ms exceeded" mean in Playwright?

The navigation started, but the page did not reach the requested load state before the navigation timeout. By default page.goto waits for load , which fires only after every image, stylesheet, script and iframe referenced by the page has finished. One slow third-party script, analytics beacon or ad iframe is enough to hold it open. The last line of the call log tells you which state was awaited.

How do I fix "page.goto: Timeout 30000ms exceeded"?

1. Open the trace (npx playwright show-trace) and check which requests were still pending at the timeout. 2. Wait for less: page.goto(url, { waitUntil: 'domcontentloaded' }) or 'commit', then wait for the element you need with expect(locator).toBeVisible(). 3. Avoid networkidle; Playwright's docs discourage it for testing. 4. Block heavy or irrelevant third-party requests with page.route() returning route.abort(). 5. Only then raise the limit with page.setDefaultNavigationTimeout() or the goto timeout option.

Run browser automation you can watch and take over

Autonoly runs each job in a real cloud Chromium browser driven by Playwright. Timeouts and detached elements still happen; the difference is that you can watch the run live, see the step that failed, and take control of the browser to finish it.

Also see Live browser control.