puppeteer/puppeteer · 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

rewriteEvaluationError catches the browser's raw 'ExecutionContext was destroyed' / 'Inspected target navigated or closed' messages and rewrites them into a human-readable form. It fires whenever a script evaluation runs against a realm/frame that no longer exists — almost always because the page navigated or was closed between scheduling and executing the function.

Source

Thrown at packages/puppeteer-core/src/bidi/util.ts:189

    if (error instanceof ProtocolError) {
      error.message += ` at ${message}`;
    } else if (error instanceof TimeoutError) {
      error.message = `Navigation timeout of ${ms} ms exceeded`;
    }
    throw error;
  };
}

/**
 * @internal
 */
export function rewriteEvaluationError(error: unknown): never {
  if (error instanceof Error) {
    if (
      error.message.includes('ExecutionContext was destroyed') ||
      error.message.includes('Inspected target navigated or closed')
    ) {
      throw new Error(
        'Execution context was destroyed, most likely because of a navigation.',
      );
    }
  }
  throw error;
}

View on GitHub (pinned to d484e21c17)

Solutions

  1. Re-acquire the page/frame handle after navigation before evaluating (waitForNavigation / waitForFunction).
  2. Use page.waitForFunction with the desired predicate instead of evaluate-after-click.
  3. Detect the navigation race and retry the evaluate on the new context.
  4. Avoid keeping ElementHandle references across navigations.

Example fix

// before
await button.click(); // triggers navigation
await page.evaluate(() => document.title);
// after
await Promise.all([
  page.waitForNavigation(),
  button.click(),
]);
await page.evaluate(() => document.title);
Defensive patterns

Strategy: retry

Validate before calling

// Acquire fresh context after navigation
await page.waitForNavigation();
await page.evaluate(fn);

Type guard

const isContextDestroyed = (e) => e instanceof Error && /Execution context was destroyed/i.test(e.message);

Try / catch

try { return await page.evaluate(fn); } catch (e) { if (isContextDestroyed(e)) { await page.waitForFunction(fn); return; } throw e; }

Prevention

When it happens

Trigger: Calling page.evaluate / frame.evaluate / elementHandle.evaluate while a navigation is in flight; clicking an element whose handler triggers navigation, then awaiting a subsequent evaluate on the old context; awaiting evaluate after page.close().

Common situations: Race between a click that navigates and the next evaluate; SPA route changes that destroy the frame; reusing an ElementHandle after a navigation; calling evaluate on a frame whose page just redirected.

Related errors


AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12). Data as JSON: /api/errors/3f2335944863630e. Report an issue: GitHub.