[BUG] Desktop app built-in browser: HTTP requests from a SharedWorker fail (fetch, XHR, importScripts) unless the origin is a preview_start dev server

Status Open
Reported on v2.1.284
Maintainer reply None cached
Activity 0 comments · opened Sep 29, 2026

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 with TypeError: Failed to fetch;
  • XMLHttpRequest fires onerror with status 0;
  • importScripts() throws NetworkError: 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

  1. Serve the folder with a plain server: python -m http.server 8766 --bind 127.0.0.1.
  2. Open http://localhost:8766/ in the built-in browser pane.
  • Actual: page fetch: OK, dedicated worker: fetch OK / importScripts OK, shared worker: connected, then shared worker: fetch FAIL TypeError: Failed to fetch and importScripts 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. A new WebSocket(...) from the SharedWorker does appear in the log.
  • Expected: the SharedWorker lines succeed, as they do in Chrome.
  1. 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

  1. Serve the folder with a plain server: python -m http.server 8766 --bind 127.0.0.1.
  2. Open http://localhost:8766/ in the built-in browser pane.
  • Actual: page fetch: OK, dedicated worker: fetch OK / importScripts OK, shared worker: connected, then shared worker: fetch FAIL TypeError: Failed to fetch and importScripts 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. A new WebSocket(...) from the SharedWorker does appear in the log.
  • Expected: the SharedWorker lines succeed, as they do in Chrome.
  1. 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_

View original on GitHub ↗