Skip to content

fix(browser): fail instead of hanging when the iframe never loads - #11253

Open
im10furry wants to merge 3 commits into
vitest-dev:mainfrom
im10furry:fix/browser-iframe-hang
Open

im10furry wants to merge 3 commits into
vitest-dev:mainfrom
im10furry:fix/browser-iframe-hang

Conversation

@im10furry

Copy link
Copy Markdown
Contributor

Description

In browser mode, an iframe that never fires load hangs the run for ever: prepareIframe awaits a promise that only iframe.onload/iframe.onerror can settle, and neither is bounded by a timer, so the pool waits for that session until the job is killed. This is the root cause of #11243 (a WebKit run under CPU pressure, one file out of 128 never reports) and of the "prepare promise never resolves" reports in #10520/#10860.

The other two steps of the same handshake are already bounded - waitForReady and the ack: in sendEventToIframe both use getIframeTimeout() - so this adds the missing one.

Reproduction

Deterministic, without CPU starvation: make the tester page wait for a subresource that never finishes. test/browser/fixtures/…-style spec in test/browser/specs/readiness.test.ts:

<!-- the load event waits for subresources, and this one never finishes -->
<script src="/never-responds.js"></script>

with a Vite middleware that never ends the response, and VITEST_BROWSER_IFRAME_TIMEOUT: '2000'.

Before the fix the run hangs and the spec only ends because Vitest's own test timeout fires:

Error: Test timed out in 20000ms.

After the fix the file fails in ~2s and the run finishes:

Failed to run the test …/basic.test.ts
The iframe "…/basic.test.ts" did not load within 2000ms. The tester page or one of its
subresources never finished loading, check the browser console and the network tab for
pending requests.

Fix

prepareIframe now rejects with that message after getIframeTimeout(), and clears the timer as soon as load or error fires. The error is rejected the same way the waitForReady timeout is - it is not dispatched as an iframeerror, because that event rejects any in-flight sendEventToIframe for unrelated iframes.

The new spec (fails instead of hanging when the iframe never loads) is the third sibling of the existing fails instead of hanging when the tester never becomes ready / …stops responding to messages, so all three handshake steps are now covered.

Also deterministic: the spec's own run of the hanging page needs no CPU starvation - the subresource is simply never answered, so the hang reproduces on every machine (this is what made the fix verifiable at all; the report notes the original was intermittent).

Deliberately not changed: the response after ack:

#11243 also suggests bounding the response: after the acknowledgement (its author patched the build with getIframeTimeout() * 10). I left it alone because the wait is intentionally unbounded - the comment above it says the work "may take any amount of time" - and any fixed multiple (10 minutes in their patch) would fail legitimate long batches. If you want that hang covered too, a liveness ping from the tester (re-pinning a timer in the orchestrator) would do it without inventing a deadline; happy to implement whichever you prefer.

Verification

  • The new spec fails without the change (hangs until the test timeout) and passes with it - checked both ways.
  • test/browser specs (--config=vitest.config.unit.mts) on chromium, firefox and webkit: 42 files passed, 1 skipped, 102 tests passed.
  • PROVIDER=playwright vitest in test/browser (78 files, chromium + firefox + webkit): 396 tests passed, identical to the same run on main.
  • pnpm typecheck and eslint: pass.

`prepareIframe` awaited a promise that only `load`/`error` could settle and
neither was bounded, so an iframe whose page or subresource never finishes
loading left the pool waiting for that session for the rest of the run
(vitest-dev#11243). `waitForReady` and the `ack:` in `sendEventToIframe` already use
`getIframeTimeout()` for exactly this reason.

The spec covers the third step of the handshake the same way the other two
are covered, with a tester page that waits for a subresource that is never
answered.
@netlify

netlify Bot commented Sep 13, 2026

Copy link
Copy Markdown

Deploy Preview for vitest-dev ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 26fd512
🔍 Latest deploy log https://app.netlify.com/projects/vitest-dev/deploys/6aacb876437caf0008475f6a
😎 Deploy Preview https://deploy-preview-11253--vitest-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@im10furry
im10furry marked this pull request as ready for review September 18, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant