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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

“Execution context was destroyed, most likely because of a navigation” means the document changed while Playwright was evaluating JavaScript or using a reference tied to the old document. Synchronize the action with the expected destination using page.waitForURL(), then query the new page. If the URL stays the same, wait for the specific UI state, assertion, or response that proves the operation finished.

What the error means

Each loaded document has a JavaScript execution context. A full navigation replaces that document and destroys its context. An in-flight page.evaluate(), an evaluation-created object, or an ElementHandle can therefore be interrupted even though a later page.url() check has not changed yet.

Playwright issue #27374 describes this timing: evaluation fails because navigation has already destroyed the context, while URL inspection can still be premature. Another report, #27406, shows the error after a logout click redirected to another domain in Playwright 1.38.1 with Chromium on macOS 13.5.2. That reproduction illustrates the mechanism; it does not establish behavior for every browser or Playwright version.

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

Fix a click or submit that should navigate

Register the URL wait and perform the triggering action together. This prevents a gap in which navigation can start before the wait is listening:

await Promise.all([
  page.waitForURL('**/dashboard'),
  page.getByRole('link', { name: 'Dashboard' }).click(),
]);

const title = await page.title();

Replace the pattern and locator with the destination and control in your test. Playwright documents page.waitForURL() for a matching main-frame URL and recommends an explicit URL when an action can trigger multiple navigations; see the Navigations guide and Page API reference.

Form submission example

await Promise.all([
  page.waitForURL('**/account'),
  page.getByRole('button', { name: 'Sign in' }).click(),
]);

await expect(page.getByRole('heading', { name: 'Account' })).toBeVisible();

The URL wait confirms the navigation target. The assertion confirms that the new document contains the page your test actually needs.

When the URL does not change

A URL wait cannot prove that a same-URL single-page application update completed. Wait for the observable result instead:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.getByRole('button', { name: 'Refresh results' }).click();
await expect(page.getByRole('status')).toHaveText('Updated');

Choose a signal that represents the behavior under test:

  • UI state: a locator becomes visible, enabled, or populated.
  • Text or accessibility state: a web assertion matches the expected message or status.
  • Network operation: wait for the specific response when the request itself is what matters.

Interactions auto-wait for target elements to become actionable, but that does not tell Playwright when your application’s asynchronous rendering is complete. Use web assertions as described in the navigation guidance.

Choosing the right Playwright wait

What must be true Use What it proves
A known destination was reached page.waitForURL(pattern) The main-frame URL matches the pattern.
A same-URL update finished Locator or web assertion The relevant element, text, or state is ready.
A tested request completed Response wait The specified network response occurred.
A browser lifecycle milestone matters domcontentloaded, load, or commit That milestone occurred, not that later application data is ready.
All network activity became quiet Do not use as a general readiness test Playwright marks networkidle discouraged for tests; assertions are preferred.

As Playwright’s Navigations guide puts it, “There is no way to tell that the page is loaded, it depends on the page, framework, etc.” A browser load event can occur before client-rendered data or controls appear.

Reacquire elements after navigation

Do not carry an ElementHandle or an object returned from evaluation across a document replacement. Those references belong to the previous page context. Use a locator after the navigation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await Promise.all([
  page.waitForURL('**/settings'),
  page.getByRole('link', { name: 'Settings' }).click(),
]);

const email = await page.getByLabel('Email').inputValue();

Locators describe how to find an element in the current page state and can resolve it again after navigation. Handles refer to particular page objects. Playwright’s guidance on evaluating JavaScript, locators, and handles explains these distinctions.

Why common “fixes” fail

Replacing synchronization with a sleep

page.waitForTimeout() waits a fixed duration but does not establish whether navigation happened or whether the required UI state is true. It can be too short on a slow run and wasteful on a fast one.

Using networkidle everywhere

Background polling, analytics, service workers, or long-lived connections can prevent a quiet network. The Page API discourages networkidle for tests and recommends web assertions instead.

Continuing with waitForNavigation()

page.waitForNavigation() is deprecated. The Page API states: “This method is inherently racy, please use page.waitForURL() instead.”

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

Checking the URL only after catching the error

A catch block can run in the narrow interval after the old context was destroyed but before the URL property reflects the new destination. Synchronize before the action rather than retrying the same evaluation blindly.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

A practical diagnosis checklist

  1. Identify the operation immediately before the failure: click, submit, logout, reload, redirect, or script-triggered navigation.
  2. Decide whether a full navigation is expected or whether the application should remain on the same URL.
  3. For a known destination, pair the action with page.waitForURL() in Promise.all.
  4. For a same-URL change, assert the resulting locator, text, state, or specific response.
  5. After a document replacement, reacquire locators and avoid old handles.
  6. Only use a lifecycle wait when that exact milestone is the behavior being tested.

Or skip the browser setup: ScreenshotNeo

If your goal is simply to capture a page rather than test a navigation workflow, ScreenshotNeo returns a screenshot or PDF through one GET request, so there is no Playwright context to synchronize. It accepts consent banners before capture and bills only clean shots; failed loads, bot checks, blank pages, timeouts, and cache hits are not billed, with the result identified by response headers. Its MCP server also exposes screenshot, page-info, and PDF tools to AI clients.

For the API parameters and the complete option list, see the ScreenshotNeo documentation. Example:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Bottom line for your test

Make the expected transition explicit. Pair a navigation-triggering action with page.waitForURL(); for same-URL behavior, assert the concrete application state; then find elements again in the new document. This removes the race that destroys the old execution context instead of masking it with a delay or a generic network wait.

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

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.