microsoft/playwright · error · Error

The page has closed

Error message

The page has closed

What it means

Thrown inside the expectScreenshot polling loop when the Page becomes closed mid-wait. The screenshot matcher retries until the image stabilizes, and each iteration re-checks page liveness; a closed page cannot produce further screenshots, so the loop aborts with this message rather than timing out.

Solutions

  1. Ensure no concurrent code path closes the page while the screenshot assertion is in flight; await the assertion before calling page.close().
  2. Increase the page/script timeout or reduce page load so the assertion completes before any teardown.
  3. Wrap the assertion and treat a 'page closed' error as a signal to re-create the page, or use a scoped fixture that owns the page lifetime.

Example fix

// before
const assert = expect(page).toHaveScreenshot('x.png');
await page.close(); // races with the assertion
await assert;
// after
await expect(page).toHaveScreenshot('x.png');
await page.close();
Defensive patterns

Strategy: try-catch

Validate before calling

if (page.isClosed()) throw new Error('page already closed; skip screenshot assertion');

Try / catch

try {
  await expect(page).toHaveScreenshot('home.png');
} catch (e) {
  if (/page has closed/i.test(e.message)) {
    // page died mid-assertion: recreate or skip rather than retry blindly
    throw new Error('Screenshot assertion aborted: page closed unexpectedly');
  }
  throw e;
}

Prevention

When it happens

Trigger: The page (or its browser) is closed, crashes, or navigates to a destroy trigger while expect(page).toHaveScreenshot() is polling; page.close() called from another async branch during the assertion; the web app calls window.close(); the browser process is killed by the OS.

Common situations: A test that closes the page in a finally block racing with a still-running screenshot assertion; an app that self-closes (popup, OAuth flow); resource exhaustion or OOM killing the browser during a heavy screenshot comparison.

Related errors


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

Appendix: source

Thrown at packages/playwright-core/src/server/page.ts:759

      const areEqualScreenshots = (actual: Buffer | undefined, expected: Buffer | undefined, previous: Buffer | undefined) => {
        const comparatorResult = actual && expected ? comparator(actual, expected, options) : undefined;
        if (comparatorResult !== undefined && !!comparatorResult === !!options.isNot)
          return true;
        if (comparatorResult)
          intermediateResult = { errorMessage: comparatorResult.errorMessage, diff: comparatorResult.diff, actual, previous };
        return false;
      };
      let actual: Buffer | undefined;
      let previous: Buffer | undefined;
      const pollIntervals = [0, 100, 250, 500];
      if (options.expected)
        progress.log(`  verifying given screenshot expectation`);
      else
        progress.log(`  generating new stable screenshot expectation`);
      let isFirstIteration = true;
      while (true) {
        if (this.isClosed())
          throw new Error('The page has closed');
        const screenshotTimeout = pollIntervals.shift() ?? 1000;
        if (screenshotTimeout)
          progress.log(`waiting ${screenshotTimeout}ms before taking screenshot`);
        previous = actual;
        actual = await rafrafScreenshot(progress, screenshotTimeout).catch(e => {
          if (this.mainFrame().isNonRetriableError(e))
            throw e;
          progress.log(`failed to take screenshot - ` + e.message);
          return undefined;
        });
        if (!actual)
          continue;
        // Compare against expectation for the first iteration.
        const expectation = options.expected && isFirstIteration ? options.expected : previous;
        if (areEqualScreenshots(actual, expectation, previous))
          break;
        if (intermediateResult)
          progress.log(intermediateResult.errorMessage);

View on GitHub (pinned to f1d33b5029)