Describe the bug
In browser mode, if a tester page's JS context dies without closing its websocket, the whole run hangs forever with no output — testTimeout, retry, and reporters all go silent until something external kills the process (locally Ctrl-C, on CI timeout-minutes).
The hang is structural — there is no run-level deadline anywhere on the per-file execution path:
- The node-side RPC to the orchestrator is created with
timeout: -1 (@vitest/browser setupClient), so orchestrator.createTesters([file]) in the pool's runNextTest can wait forever. Pending calls are only rejected by the websocket close handler ("Browser connection was closed while running tests").
- In the page, the orchestrator's
sendEventToIframe({ event: 'execute' }) returns a promise that resolves only when the tester posts response:execute on the BroadcastChannel — no timeout, and only an iframeerror event can reject it.
testTimeout and retry are enforced inside the tester iframe's own JS, so once that context is gone they can never fire.
browser.connectTimeout only covers session creation; nothing covers "tester died mid-file with the socket still open."
Real triggers we hit (all funnel into the same unbounded wait):
Reproduction
Deterministic illustration of the missing deadline (any browser-mode project, playwright provider):
vitest run a suite with enough files to take ~30s.
- Mid-run, freeze the browser:
pkill -STOP -f headless_shell (the websocket stays open, so the close-handler rescue never fires).
- The run produces no further output and never times out —
testTimeout cannot fire because it lives inside the frozen page.
The organic version (optimizer-reload trigger) is racy by nature: cold optimizer cache + a dep that misses the initial scan → mid-run re-optimize → full reload → hang. Observed repeatedly on a ~110-file / ~850-test storybook (@storybook/addon-vitest) suite.
Expected behavior
A tester that stops responding should fail that test file (and let the run continue or fail fast), not hang the run forever. Possible shapes: a configurable deadline around createTesters per file, or a heartbeat between orchestrator/tester and the node side that converts a dead-but-connected session into a file-level error.
Related: #9509 (load-time flakes), #10520 (hang at collection), #8447 / #7333 (unexpected reload warning), #9437 (disk accumulation — one of the triggers).
System Info
- vitest / @vitest/browser / @vitest/browser-playwright 4.1.10 (also reproduced conceptually against current dist)
- provider: playwright, chromium headless
- observed on macOS 15 (arm64), ubuntu 24 (x64 GitHub runners, docker arm64)
- node 24
Used Package Manager
pnpm
Describe the bug
In browser mode, if a tester page's JS context dies without closing its websocket, the whole run hangs forever with no output —
testTimeout,retry, and reporters all go silent until something external kills the process (locally Ctrl-C, on CItimeout-minutes).The hang is structural — there is no run-level deadline anywhere on the per-file execution path:
timeout: -1(@vitest/browsersetupClient), soorchestrator.createTesters([file])in the pool'srunNextTestcan wait forever. Pending calls are only rejected by the websocketclosehandler ("Browser connection was closed while running tests").sendEventToIframe({ event: 'execute' })returns a promise that resolves only when the tester postsresponse:executeon the BroadcastChannel — no timeout, and only aniframeerrorevent can reject it.testTimeoutandretryare enforced inside the tester iframe's own JS, so once that context is gone they can never fire.browser.connectTimeoutonly covers session creation; nothing covers "tester died mid-file with the socket still open."Real triggers we hit (all funnel into the same unbounded wait):
timeout-minutes: 20cancelled the job.Reproduction
Deterministic illustration of the missing deadline (any browser-mode project, playwright provider):
vitest runa suite with enough files to take ~30s.pkill -STOP -f headless_shell(the websocket stays open, so the close-handler rescue never fires).testTimeoutcannot fire because it lives inside the frozen page.The organic version (optimizer-reload trigger) is racy by nature: cold optimizer cache + a dep that misses the initial scan → mid-run re-optimize → full reload → hang. Observed repeatedly on a ~110-file / ~850-test storybook (
@storybook/addon-vitest) suite.Expected behavior
A tester that stops responding should fail that test file (and let the run continue or fail fast), not hang the run forever. Possible shapes: a configurable deadline around
createTestersper file, or a heartbeat between orchestrator/tester and the node side that converts a dead-but-connected session into a file-level error.Related: #9509 (load-time flakes), #10520 (hang at collection), #8447 / #7333 (unexpected reload warning), #9437 (disk accumulation — one of the triggers).
System Info
Used Package Manager
pnpm