Skip to content

fix(wasm-sw): boot shell waits for the worker's claim before loading the app - #463

Merged
gg582 merged 1 commit into
devfrom
fix/wasm-sw-boot-race
Oct 29, 2026
Merged

gg582 merged 1 commit into
devfrom
fix/wasm-sw-boot-race

Conversation

@gg582

@gg582 gg582 commented Oct 29, 2026

Copy link
Copy Markdown
Member

Summary

Fixes the intermittent wasm-sw-browser-test failure, page.waitForSelector: Timeout 30000ms exceeded … 'text=Session visits'. It failed 6 times on 10-27/10-29, on dev pushes and PRs alike (e.g. #461, #462), always at the same step. It is not a test flake: a race in the example's boot shell (example/wasm-service-worker/index.html) can leave a real user on the static shell too.

Cause

The shell called register('/sw.js'), and if no worker controlled the page yet it reloaded immediately, before the worker had installed. The reload loops until the worker is active. The worker's activate handler then runs clients.claim().

The problem is when that claim lands while a later shell is still awaiting register(). For an existing registration, register() waits on the /sw.js update check. That shell then sees a controller, prints "Service worker active." and returns. Nothing reloads it, so the static shell stays up and the CWIST page ("Session visits") never appears. The test's controller !== null wait passes and the selector wait times out, which matches the CI log.

Fix

The shell is only ever what the network serves. A controlled / is answered by CWIST through the worker, and the worker's cache serves the file only as /index.html. Whenever the shell's script runs, the app has therefore not been loaded:

  • Controller already present (claimed during register(), or opened as /index.html): location.replace('/').
  • No controller yet: wait for controllerchange (the claim), then location.replace('/').

The test now reports where it stopped instead of a bare selector timeout: URL, whether the page is controlled, the shell's status text, and worker errors.

Verification (local, Chromium via Playwright 1.63, the CI pin)

  • Reproduction before the fix: a copy of the test delays the /sw.js update check and the core .wasm fetches by 3 s, as on a busy runner. 2 of 3 runs got stuck with exactly the CI state: shell status "Service worker active.", controlled=true, no CWIST page.
  • Same harness after the fix: 6/6 pass.
  • Unmodified test after the fix: 5/5 pass.
  • No reload loop: opening /index.html once the worker is installed lands on the CWIST page at / after 2 navigations.

…the app

The boot shell reloaded as soon as register() resolved without a
controller, before the worker had installed. When the worker's
clients.claim() then landed while a later shell was still awaiting
register() (which waits on the /sw.js update check), that shell saw a
controller, printed "Service worker active." and stopped: the page stayed
the static shell and the CWIST app never loaded. This is the intermittent
`wasm-sw-browser-test` timeout on "Session visits" (6 CI failures on
10-27/10-29, all at that step).

The shell is only ever what the network serves (a controlled "/" is
answered by CWIST, and the cache serves it only as /index.html), so:
with a controller, load "/" right away; without one, wait for
controllerchange and then load it.

The test now reports where it stopped (URL, controlled, shell status,
worker errors) instead of a bare selector timeout.

Reproduced locally by delaying the /sw.js update check and the core
.wasm fetches by 3 s: 2 of 3 runs stuck before the fix, 6 of 6 pass after.
@gg582
gg582 merged commit 125ff6a into dev Oct 29, 2026
47 of 48 checks passed
gg582 added a commit that referenced this pull request Oct 29, 2026
cherry-pick: wasm-sw boot shell waits for the worker's claim (#463) to main
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