[BUG] Desktop app built-in browser: HTTP requests from a SharedWorker fail (fetch, XHR, importScripts) unless the origin is a preview_start dev server
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Title: Desktop app built-in browser: HTTP requests from a SharedWorker fail (fetch, XHR, importScripts) unless the origin is a preview_start dev server
Environment
- Claude desktop app 2.9939.4 (MSIX), Code tab, built-in browser pane
- User agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Claude/2.9939.4 Chrome/152.0.7977.130 Safari/537.36 MSIX - Windows 11 Enterprise 10.0.26200
Summary
In the built-in browser pane, every HTTP request made from inside a SharedWorker fails:
fetch()rejects withTypeError: Failed to fetch;XMLHttpRequestfiresonerrorwith status 0;importScripts()throwsNetworkError: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at '…' failed to load.
The requests never reach the server. The same requests from the page and from a dedicated Worker succeed. The SharedWorker's own script loads, and a WebSocket opened from the SharedWorker does reach the server. Everything works when the same page is served through preview_start. That suggests the pane's per-origin network permission isn't applied to requests from SharedWorker contexts.
Impact: web apps that run their realtime connection in a SharedWorker never finish loading in the pane. Our tool's web client (SignalR in a SharedWorker at /host/web/signalr-shared-worker/) signs in, then sits on its loading bar forever. It works in regular Chrome on the same PC.
Minimal repro: five files in one folder.
index.html
<!doctype html><meta charset="utf-8"><pre id="out"></pre>
<script>
const log = m => { out.textContent += m + '\n'; console.log(m); };
fetch('ping.txt').then(r => r.text()).then(t => log('page fetch: OK ' + t.trim()), e => log('page fetch: FAIL ' + e));
const dw = new Worker('dedicated.js'); dw.onmessage = e => log('dedicated worker: ' + e.data);
const sw = new SharedWorker('shared.js'); sw.port.onmessage = e => log('shared worker: ' + e.data); sw.port.start();
</script>
dedicated.js
fetch('ping.txt').then(r => r.text()).then(t => postMessage('fetch OK ' + t.trim()), e => postMessage('fetch FAIL ' + e));
try { importScripts('lib.js'); postMessage('importScripts OK'); } catch (e) { postMessage('importScripts FAIL ' + e); }
shared.js
onconnect = ev => { const port = ev.ports[0]; port.postMessage('connected');
fetch('ping.txt').then(r => r.text()).then(t => port.postMessage('fetch OK ' + t.trim()), e => port.postMessage('fetch FAIL ' + e));
try { importScripts('lib.js'); port.postMessage('importScripts OK'); } catch (e) { port.postMessage('importScripts FAIL ' + e); } };
lib.js: self.libLoaded = true;ping.txt: pong
Steps
- Serve the folder with a plain server:
python -m http.server 8766 --bind 127.0.0.1. - Open
http://localhost:8766/in the built-in browser pane.
- Actual:
page fetch: OK,dedicated worker: fetch OK / importScripts OK,shared worker: connected, thenshared worker: fetch FAIL TypeError: Failed to fetchandimportScripts FAIL NetworkError …. - Server log:
GET /,/ping.txt,/shared.js,/dedicated.js,/lib.js(the dedicated worker's), and nothing from the SharedWorker's fetch or importScripts. Anew WebSocket(...)from the SharedWorker does appear in the log. - Expected: the SharedWorker lines succeed, as they do in Chrome.
- Serve the same folder through
preview_start(.claude/launch.json, port 8765) and open it in the pane. All lines succeed.
Also seen on a real app's origin (http://localhost, port 80): a blob-URL SharedWorker created from the page fails all three request types, while a dedicated Worker from the same blob gets HTTP 200.
What Should Happen?
Requests made from inside a SharedWorker should behave the same as requests from the page and from a dedicated Worker. In the repro, fetch('ping.txt') in the SharedWorker should resolve to pong, and importScripts('lib.js') should load, just as they do in the dedicated worker and in regular Chrome. The requests should reach the server, and the result shouldn't depend on whether the page is served by preview_start or by any other local server. Web apps that run their realtime connection in a SharedWorker (for example our web client, which runs SignalR from /host/web/signalr-shared-worker/) should then finish loading in the built-in browser instead of hanging on their loading screen.
Error Messages/Logs
Steps to Reproduce
Minimal repro: five files in one folder.
index.html
<!doctype html><meta charset="utf-8"><pre id="out"></pre>
<script>
const log = m => { out.textContent += m + '\n'; console.log(m); };
fetch('ping.txt').then(r => r.text()).then(t => log('page fetch: OK ' + t.trim()), e => log('page fetch: FAIL ' + e));
const dw = new Worker('dedicated.js'); dw.onmessage = e => log('dedicated worker: ' + e.data);
const sw = new SharedWorker('shared.js'); sw.port.onmessage = e => log('shared worker: ' + e.data); sw.port.start();
</script>
dedicated.js
fetch('ping.txt').then(r => r.text()).then(t => postMessage('fetch OK ' + t.trim()), e => postMessage('fetch FAIL ' + e));
try { importScripts('lib.js'); postMessage('importScripts OK'); } catch (e) { postMessage('importScripts FAIL ' + e); }
shared.js
onconnect = ev => { const port = ev.ports[0]; port.postMessage('connected');
fetch('ping.txt').then(r => r.text()).then(t => port.postMessage('fetch OK ' + t.trim()), e => port.postMessage('fetch FAIL ' + e));
try { importScripts('lib.js'); port.postMessage('importScripts OK'); } catch (e) { port.postMessage('importScripts FAIL ' + e); } };
lib.js: self.libLoaded = true;ping.txt: pong
Steps
- Serve the folder with a plain server:
python -m http.server 8766 --bind 127.0.0.1. - Open
http://localhost:8766/in the built-in browser pane.
- Actual:
page fetch: OK,dedicated worker: fetch OK / importScripts OK,shared worker: connected, thenshared worker: fetch FAIL TypeError: Failed to fetchandimportScripts FAIL NetworkError …. - Server log:
GET /,/ping.txt,/shared.js,/dedicated.js,/lib.js(the dedicated worker's), and nothing from the SharedWorker's fetch or importScripts. Anew WebSocket(...)from the SharedWorker does appear in the log. - Expected: the SharedWorker lines succeed, as they do in Chrome.
- Serve the same folder through
preview_start(.claude/launch.json, port 8765) and open it in the pane. All lines succeed.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.284
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
_No response_