Browser pane: service-worker registration fails — script fetch never leaves the app
Environment: Claude Code desktop 2.1.227, macOS 25.6.0. Pane UA: Claude/1.28929.0 Chrome/148.0.7778.280 Electron/42.7.0.
Symptom: In any Browser-pane tab, navigator.serviceWorker.register() rejects withTypeError: Failed to register a ServiceWorker for scope ('http://localhost:8931/') with script ('http://localhost:8931/sw.js'): An unknown error occurred when fetching the script.
Minimal repro: Serve a one-line sw.js with python3 -m http.server 8931, open the page in the pane, call navigator.serviceWorker.register('/sw.js').
Evidence it dies pre-network, pane-side:
- Page-context
fetch('/sw.js')→ 200text/javascript; the server access log shows that request. - The log shows zero requests for either
register()attempt (plain and cache-busted withupdateViaCache: 'none') — the SW script fetch never reaches the server. - Fresh origin (no prior state), top-level frame (
window.top === window), secure context, CacheStorage andstorage.estimate()healthy. - Reproduced on three localhost servers/ports (vite ×2, python ×1). Real Chrome on the same machine registers the same files first try.
- Regression: MSW-based dev workflows registered fine in the pane through ~2026-08-02.
- Persists across a full app relaunch (Cmd+Q → reopen, still 2.1.227): identical failure on re-test.
Impact: Any app needing a service worker in the pane (MSW mock-mode dev, PWAs) cannot boot; verification falls back to a real browser.
Guess: The pane session's request interception (origin-approval/browsing-policy layer) doesn't handle the service-worker script fetch context, so the browser-process fetch is dropped.
3 Comments
Additional data point: the failure is ServiceWorker-specific, not a general worker-script-fetch drop.
Ran a controlled repro in the Browser pane against a local
python3 -m http.server, creating five worker types from the same origin in a single page:| Worker | Script source | Result |
|---|---|---|
|
new Worker('/dedicated.js')| HTTP | ✅ ran ||
new Worker('/m.js', { type: 'module' })| HTTP | ✅ ran ||
new SharedWorker('/shared.js')| HTTP | ✅ ran ||
new Worker(blob: URL)| blob | ✅ ran ||
navigator.serviceWorker.register('/sw.js')| HTTP | ❌TypeError: ...An unknown error occurred when fetching the script.|Dedicated and shared workers fetch their scripts over HTTP from the same origin and start fine in the pane; only the ServiceWorker script fetch is dropped. That narrows the "request-interception layer doesn't handle the worker-script fetch context" hypothesis specifically to the ServiceWorker fetch path — dedicated/shared worker script fetches are handled correctly.
Practical implication: apps whose local persistence uses a DedicatedWorker/SharedWorker (e.g. OPFS SQLite) are unaffected and boot in the pane; only ServiceWorker-dependent flows (MSW mock mode, PWA install/offline) break. Same environment notes as the original report (secure context, top-level frame; real Chrome registers the same files first try).
<details>
<summary>Repro (serve this dir with <code>python3 -m http.server 8931 --bind 127.0.0.1</code>, open <code>/index.html</code> in the pane)</summary>
Sibling scripts:
dedicated.js=self.postMessage('dedicated-worker-ran');module-worker.js= same;shared.js=self.onconnect = (e) => e.ports[0].postMessage('shared-worker-ran');sw.js=self.addEventListener('install', () => self.skipWaiting()).</details>
**Follow-up: a second worker-context fetch drop — SharedWorker subresource fetches also fail (not just ServiceWorker script fetches).**
Chasing why a dev app wouldn't boot in the pane, I isolated another case in the same family. The pane drops HTTP module/subresource fetches initiated from a SharedWorker context, while the identical fetch from a dedicated worker succeeds.
Isolation matrix (all same-origin, local
python3 -m http.server, run in the pane):| Case | Pane |
|---|---|
| Dedicated worker imports an HTTP module (static or dynamic
import()) | ✅ works || Self-contained SharedWorker, no imports (
onconnect+ MessagePort) | ✅ works || SharedWorker imports an HTTP module (static top-level OR dynamic
import()) | ❌ fails — opaqueonerror; dynamic form logsFailed to fetch dynamically imported module: http://127.0.0.1:8932/dep.js|| ServiceWorker
register('/sw.js')| ❌ fails (this issue) |So it isn't dynamic-vs-static and it isn't "workers can't fetch" — a dedicated worker fetches HTTP modules fine. It's specifically the SharedWorker context whose subresource fetch is dropped, mirroring the ServiceWorker script-fetch drop. Real Chrome on the same machine loads all of these first try.
Minimal repro:
Real-world impact: apps whose local-persistence/DB layer runs a SharedWorker that loads modules over HTTP (common in Vite/webpack dev mode — e.g. an OPFS-SQLite worker) get an opaque
SharedWorker errorand never finish booting in the pane; the DB never opens. Production builds that fully bundle the SharedWorker (no HTTP subresource import) are unaffected, which is why it presents as dev-only and works in a real browser. Suggests the fix should cover the SharedWorker fetch context alongside the ServiceWorker one.Forgot to mention that this is a regression. It was introduced sometime in the last two weeks or so.