[BUG] Claude Desktop 1.20186.9.0 (Windows MSIX): unreachable preview URL causes >1GB/sec allocation that blocks the main process; watchdog reload re-triggers it in an infinite hang loop

Open 💬 0 comments Opened Jul 14, 2026 by Coamithra

TL;DR

If a preview card points at a dev server it can't actually reach, Claude Desktop doesn't show an error — it melts. The preview webview spins on "Loading…" and starts allocating over 1 GB/sec, which blocks the Electron main process and freezes the entire app. The watchdog then kills and reloads the webview — but the reload restores the same session, which re-embeds the same broken preview, which explodes again. Infinite hang/reload loop, getting faster each round, until you fully quit the app.

In my case the preview was unreachable because Vite on Windows bound to IPv6 ::1 only, so 127.0.0.1:<port> was connection-refused. That part is Vite's quirk, not Anthropic's bug. The bug is that an unreachable localhost port can take down the whole application. A dead port should render "couldn't connect", not a denial of service.

Preflight Checklist

  • [x] I have searched existing issues
  • [x] This is a single bug report
  • [x] I am using the latest version

Environment

  • App: Claude Desktop 1.20186.9.0, Windows 11, MSIX (Store), Claude_pzs8sxrjxfjjc
  • Machine: 64 GB RAM (33 GB free throughout — this was never system memory pressure)

Reproduction

  1. Start a dev server that is not reachable at the URL the preview embeds. I hit this naturally: npm run dev -- --port 5221 --strictPort (Vite) bound to ::1 only on Windows. Verified:
  • http://127.0.0.1:5221 → connection refused
  • http://[::1]:5221 → HTTP 200
  1. Open the resulting preview card in a Claude Code-in-Desktop session (route claude.ai/epitaxy/local_<uuid>).
  2. Watch the app die.

Should also reproduce by pointing a preview at any dead localhost port.

Expected

Preview shows a connection error. Failure is contained to the preview.

Actual

The preview sits on "Loading…" forever while the renderer allocates unboundedly and pegs ~5 cores. Two separate renderer processes were caught at 16.4 GB (in ~50 s from spawn) and 21 GB working set before OOM-ing.

This blocks the main process — which is single-threaded and brokers all UI/IPC — so every window freezes. From logs\main.log, event-loop stalls double each round with main RSS tracking right alongside:

| Time | Main thread blocked | Main RSS |
|---|---|---|
| 14:33:18 | 564 ms | 808 MB |
| 14:33:24 | 1,997 ms | 1,538 MB |
| 14:33:33 | 5,223 ms | 2,710 MB |
| 14:34:02 | 19,419 ms | 6,007 MB |
| 14:34:46 | 43,227 ms | 8,961 MB |
| 14:37:17 | 151,279 ms | 4,340 MB |

Timing correlation is tight: the Vite server started at 14:33:10; the first stall hit at 14:33:18 — 8 seconds later.

The loop (the part I'd most like fixed)

[warn]  [event-loop-stall] main process blocked for 19230ms ... rss 5229MB
[error] Sentry caught: { value: 'Main webview became unresponsive' }
[info]  Main webview is unresponsive, will kill and reload

The watchdog reload restores the same session route → re-embeds the same broken preview → explodes again. Four episodes in ten minutes, accelerating: 14:33 → 14:38 → 14:42 → 14:43. There's no backoff and no circuit breaker, so the recovery mechanism is what makes it self-sustaining.

Main-process RSS also never returns to baseline across reloads (326 MB at launch → 10.8 GB); only a full app restart clears it.

Note the app's own [likely sleep: duration_heuristic] tag mis-attributes these stalls to the machine sleeping. It wasn't asleep — RSS was exploding.

Asks

  1. A failed preview load must not allocate unboundedly — cap it, and fail the load instead of retry-storming.
  2. Contain preview failures to the preview. A dead localhost port shouldn't be able to block the main process.
  3. Give the unresponsive-webview watchdog a circuit breaker, so it doesn't reload straight back into the state that just killed it.

Evidence

  • Sentry event IDs (these are the exact stalls, already in your telemetry): a75df8618ecb4ac5b28de574bb235270, 5437750ebea444438b6fb9ad8449e19c
  • Logs + Crashpad dumps: %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\{logs,Crashpad}

Possibly related (same symptom, different trigger)

  • #76725 — kill-and-reload loop on Windows MSIX, but triggered at cold start (Cowork warm-lifecycle), not by a preview.
  • #75226 — main-process event loop repeatedly blocked.

If maintainers think this is the same underlying fragility as those, happy to have it folded in — but the trigger here is specific and reproducible on demand, which may make it the easiest of the three to chase down.

Minor, noticed while debugging (not worth separate reports)

  • MaxListenersExceededWarning on eIPC channels (claude.settings_$_AppPreferences_$_preferencesChanged, AutoUpdater.updaterState) — 11 listeners and climbing, accumulating on each webview reload. Probably compounds the leak the longer the loop runs.
  • Every line in main.log is written twice (duplicate logger transport).

View original on GitHub ↗