microsoft/playwright · error · Error

Execution context was destroyed, most likely because of a na

Error message

Execution context was destroyed, most likely because of a navigation.

What it means

The catch-all branch of rewriteError() (crExecutionContext.ts:113): if the failure is neither a JavaScript error raised by the page nor a known session-closed error, Playwright assumes the isolated-world execution context was destroyed — almost always because of a navigation or context disposal mid-evaluate.

Source

Thrown at packages/playwright-core/src/server/chromium/crExecutionContext.ts:113

  }

  shouldPrependErrorPrefix(): boolean {
    return false;
  }
}

function rewriteError(error: Error): Protocol.Runtime.evaluateReturnValue {
  if (error.message.includes('Object reference chain is too long') || error.message.includes('CBOR: stack limit exceeded'))
    throw new Error('Cannot serialize result: object reference chain is too long.');
  if (error.message.includes('Object couldn\'t be returned by value'))
    return { result: { type: 'undefined' } };
  if (error.message.includes('Promise was collected'))
    throw new Error('Resulting promise was garbage collected.');

  if (error instanceof TypeError && error.message.startsWith('Converting circular structure to JSON'))
    rewriteErrorMessage(error, error.message + ' Are you passing a nested JSHandle?');
  if (!js.isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error))
    throw new Error('Execution context was destroyed, most likely because of a navigation.');
  throw error;
}

function potentiallyUnserializableValue(remoteObject: Protocol.Runtime.RemoteObject): any {
  const value = remoteObject.value;
  const unserializableValue = remoteObject.unserializableValue;
  return unserializableValue ? js.parseUnserializableValue(unserializableValue) : value;
}

function renderPreview(object: Protocol.Runtime.RemoteObject): string | undefined {
  if (object.type === 'undefined')
    return 'undefined';
  if ('value' in object)
    return String(object.value);
  if (object.unserializableValue)
    return String(object.unserializableValue);

  if (object.description === 'Object' && object.preview) {

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Await the navigation (page.waitForLoadState or waitForURL) before evaluating.
  2. Use page.waitForFunction so the predicate is retried in the new context.
  3. Retry the evaluate once when this specific message is caught.

Example fix

// before
await page.click('#submit');
const v = await page.evaluate(() => window.result);
// after
await Promise.all([ page.waitForNavigation(), page.click('#submit') ]);
await page.waitForLoadState('domcontentloaded');
const v = await page.evaluate(() => window.result);
Defensive patterns

Strategy: retry

Validate before calling

// Stabilize before evaluating after a navigation trigger.
await page.waitForLoadState('domcontentloaded');
const v = await page.evaluate(() => (window as any).result);

Try / catch

async function evalStable(page: Page, fn: string) {
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      return await page.evaluate(fn);
    } catch (e) {
      if (!/Execution context was destroyed/.test(String((e as Error).message)) || attempt === 2) throw e;
      await page.waitForLoadState('domcontentloaded');
    }
  }
}

Prevention

When it happens

Trigger: Calling page.evaluate / locator.evaluate during a navigation that recreates the frame's context; clicking a link that navigates and then evaluating on the stale context; frame removed mid-script.

Common situations: Missing await on a click that triggers navigation, then evaluate runs against the destroyed context; client-side routing that swaps the document; popups that navigate away.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/d995949957122e62. Report an issue: GitHub.