Skip to content

[Bug]: context.unrouteAll() never returns when a continued route makes the page reload #43014

Closed as duplicate

Description

Version

1.63.0 (also 1.57.0)

Steps to reproduce

  1. npm i playwright@1.63.0 && npx playwright install chromium-headless-shell
  2. Save the script below as repro.mjs and run it (node repro.mjs; I ran it with bun repro.mjs, Bun 1.4.2).
  3. After some hundreds of iterations it prints iteration N: context.unrouteAll() has not returned after 10 s.
import http from 'node:http';
import { chromium } from 'playwright';

// A page that dynamically imports a few modules and reloads if one fails, the
// way a Vite app reloads on `vite:preloadError`.
const server = http.createServer((req, res) => {
  if (req.url === '/') {
    const imports = Array.from({ length: 8 }, (_, i) => `import('/chunk-${i}.js')`).join(', ');
    res.setHeader('content-type', 'text/html');
    res.end(`<script type="module">Promise.all([${imports}]).catch(() => location.reload());</script>`);
    return;
  }
  // Slow enough that some imports are still being routed when unrouteAll runs.
  setTimeout(() => {
    res.setHeader('content-type', 'text/javascript');
    res.end('export default 1;');
  }, 20 + Math.random() * 40);
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const origin = `http://127.0.0.1:${server.address().port}`;

// site.test does not resolve, so a request the route stops answering fails.
const browser = await chromium.launch({ args: ['--host-resolver-rules=MAP site.test ~NOTFOUND'] });
console.log(`chromium ${browser.version()}`);
for (let i = 1; i <= 3000; i++) {
  const context = await browser.newContext();
  const page = await context.newPage();
  await context.route('**/*', async (route) => {
    const response = await context.request.get(origin + new URL(route.request().url()).pathname);
    await route.fulfill({ response });
  });
  await page.goto('https://site.test/', { waitUntil: 'commit' });
  await new Promise((resolve) => setTimeout(resolve, Math.random() * 60));
  const timer = setTimeout(() => {
    console.log(`iteration ${i}: context.unrouteAll() has not returned after 10 s`);
    process.exit(1);
  }, 10_000);
  await context.unrouteAll({ behavior: 'ignoreErrors' });
  clearTimeout(timer);
  await context.close();
}
console.log('no hang in 3000 iterations');
await browser.close();
server.close();

Expected behavior

context.unrouteAll() returns.

Actual behavior

context.unrouteAll() never returns. The browser and the script sit idle.

Iterations at which it hung, one run each: 333, 1123 and 1646 on 1.63.0 (Chromium 153.0.8010.12); 61 and 192 on 1.57.0 (Chromium 143.0.7499.4).

Additional context

The pw:protocol log of a hanging iteration on 1.63.0, page session only, in order:

  1. unrouteAll sends Fetch.continueRequest for the five requests whose handlers are still awaiting context.request.get. All five are answered.
  2. It sends Network.setCacheDisabled {cacheDisabled: false} and Fetch.disable.
  3. The first continued request fails with net::ERR_NAME_NOT_RESOLVED. The page's catch calls location.reload(): Page.frameScheduledNavigation, Page.frameRequestedNavigation, Page.frameStartedNavigating and Network.requestWillBeSent for GET https://site.test/ arrive within the same millisecond as step 2.
  4. Fetch.disable is answered 1 ms later. Network.setCacheDisabled is never answered. No further event arrives for the reload's request: no Fetch.requestPaused, no Network.loadingFailed, no Page.frameNavigated.

So the reload that starts while interception is being switched off never finishes, and unrouteAll waits on Network.setCacheDisabled, which has no timeout.

On 1.57.0, in a larger page that keeps a timer running, I probed the page session during the hang. Page.getNavigationHistory was answered. Runtime.evaluate and a second Network.setCacheDisabled got no answer. The old document kept running, and its timer kept sending requests every 3 s. It looks as if Chromium holds every command bound for the renderer while that navigation is pending, and the navigation never proceeds.

Closing the page before unrouteAll / context.close() avoids it in my case: no routed request is continued, so the page never navigates.

The runtime does not look involved: during the hang the protocol log shows the pipe still carrying events and answers in both directions.

Environment

OS: Pop!_OS 22.04 (Linux 7.1.1, x86_64), 24 cores
Playwright: 1.63.0 (chromium-headless-shell 153.0.8010.12); also 1.57.0 (143.0.7499.4)
Runtime: Bun 1.4.2

Activity

  1. dgozman commented on Oct 1, 2026

    @dgozman
    Collaborator

    I was not able to reproduce this. Most likely, this issue has the same underlying problem as #41802, and so it should be fixed with the next v1.64 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions