How to fix "Evaluation was aborted, probably because page refresh happened" in Playwright?

Playwright

This error means a JavaScript evaluation—like page.evaluate() or page.evaluateHandle()—was interrupted because the page navigated or refreshed while the script was still executing. Unlike network-layer errors such as net::ERR_ABORTED, this is a JS execution-context issue: the browser destroys the execution context when navigation begins, leaving your evaluate call hanging with no result to return. This is a race condition between your in-flight evaluation and unexpected page navigation.

Why this differs from net::ERR_ABORTED

net::ERR_ABORTED happens at the network layer—a resource request is cancelled before it completes. "Evaluation was aborted" happens at the JavaScript layer—the browser engine is tearing down the page's JS context mid-execution. If you see ERR_ABORTED, check network conditions; if you see "evaluation was aborted," look for navigation races.

Common causes

Auto-refreshing pages: Pages with <meta http-equiv="refresh"> or setInterval(location.reload, ...) will trigger navigation while your evaluate is running.

// This can race if the page auto-refreshes
await page.evaluate(() => {
  return document.body.innerText;
});

Form submissions triggering navigation: A form submit or programmatic redirect (window.location = ...) happens during your evaluate call.

// The button click triggers a redirect, aborting the evaluate
await page.evaluate(() => {
  document.querySelector('button').click();
});

Client-side navigation libraries: Single-page app routers that navigate without full page reloads can still destroy the evaluation context.

The fix: sequence operations carefully

Separate navigation from evaluation: Run your evaluate call before or after navigation, not during it.

// Good: evaluate first, then navigate
const data = await page.evaluate(() => {
  return { title: document.title, url: window.location.href };
});
await page.goto('https://next-page.com');

Use waitForLoadState to gate evaluation: After navigation, wait for the page to stabilize before evaluating.

await page.goto('https://example.com');
await page.waitForLoadState('networkidle');
// Now safe to evaluate
const content = await page.evaluate(() => document.body.innerText);

Avoid evaluate during known navigation windows: If you know a user action (button click, form submit) will trigger navigation, don't evaluate in the same call.

// Wrong: clicking and evaluating in one go
await page.evaluate(() => {
  document.querySelector('form').submit();
  return document.title; // May not complete before navigation
});

// Right: click, wait for navigation, then evaluate
await page.click('form button[type="submit"]');
await page.waitForNavigation();
await page.waitForLoadState('networkidle');
const title = await page.evaluate(() => document.title);

Catch and retry pattern: If you expect navigation races, wrap evaluate in a try-catch and retry on abort.

async function evaluateWithRetry(page, fn, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await page.evaluate(fn);
    } catch (err) {
      if (err.message.includes('Evaluation was aborted')) {
        if (i === retries - 1) throw err;
        await page.waitForLoadState('networkidle');
        continue;
      }
      throw err;
    }
  }
}

Why it works

When you await navigation or waitForLoadState before evaluate, you guarantee the page's JS context is stable and ready. When you separate navigation from evaluation, you eliminate the race. The retry pattern lets transient aborts recover gracefully by re-establishing a fresh context on retry.

Tips

  • Use page.waitForLoadState('networkidle') or 'domcontentloaded' after navigation to ensure the context is ready before evaluate.
  • Avoid calling page.click() or other actions that may trigger navigation inside an evaluate() callback—move them outside.
  • If the page auto-refreshes, use page.removeEventListener or disable the refresh mechanism before evaluating.
  • Monitor your logs for "Evaluation was aborted" messages; they almost always indicate a navigation race.
  • For network-layer request aborts (net::ERR_ABORTED), see How to fix net::ERR_ABORTED during page.goto.