[Windows] Desktop app freezes permanently on notification events - main thread blocked in synchronous COM call into hung notification service (WpnUserService)

Status Open
Reported on v2.1.209
Maintainer reply None cached
Activity 1 comment · opened Jul 18, 2026

Summary

On Windows, when the desktop app fires a notification (response completion, tool-permission request), it does so via a synchronous cross-process COM call on the Electron main thread. If the Windows Push Notifications User Service (WpnUserService) is hung — which on my machine persisted for an entire boot — that COM call never returns, and the app's main thread blocks forever inside COM's STA modal loop. The app becomes permanently unusable and must be killed. The stuck call is never cancelled even if the wedged service is killed and restarted.

This looks like the Windows analog of #57706 (macOS: Cowork freeze via synchronous XPC notification deadlock).

Environment

  • Claude desktop app 1.22209.0.0 (MSIX/Store install), bundled Claude Code CLI 2.1.209
  • Windows 11 Pro build 26200
  • Four occurrences in one day, across two WpnUserService instances (it wedged, was restarted, and wedged again)

Symptoms

  • The moment a running response completes, the entire window stops accepting input: clicks, Esc, typing, window drag, and taskbar right-click → Close are all ignored.
  • The window is not flagged "Not Responding" in Task Manager — the COM modal loop keeps pumping just enough messages — and the renderer keeps animating normally. The status line kept ticking (e.g. "50m 7s · 3.2k tokens · 2 running tasks") for a query that had actually finished 47 minutes earlier.
  • In-flight backend work completes fine (CLI child processes go idle); nothing new can start.
  • In the app's main.log, the [process-memory] heartbeat (every ~60 s) stops dead immediately after [Stop hook] Query completed for session … — no further main-process activity, ever.

Diagnosis (performed live while frozen)

Main-thread stack (WinDbg, non-invasive attach to the Electron main process):

win32u!NtUserMsgWaitForMultipleObjectsEx
combase!CCliModalLoop::BlockFn
combase!ModalLoop
combase!ClassicSTAThreadDispatchCrossApartmentCall
combase!CSyncClientCall::SendReceive2
combase!ClassicSTAThreadSendReceive
RPCRT4!NdrpClientCall3
combase!ObjectStublessClient
Claude.exe (Electron/Node native frames)
... V8/JS frames ... uv_run ...   (Electron main loop)

Wait Chain Traversal (advapi32 WCT) of the frozen process:

TID 27728 : [Thread pid=12256 tid=27728 Blocked] → [COM] → [Thread pid=12256 tid=23772 Blocked] → [COM] → [Thread pid=10208 tid=30940 Blocked]

PID 10208 = svchost.exe -k UnistackSvcGroup -s WpnUserService (Windows Push Notifications User Service). Its thread 30940 was dead-blocked; a second wpn thread was itself stuck in a COM call to taskhostw.exe.

Repeat occurrences — no user-side toggle prevents it

Four freezes in one day, all with the identical wait-chain signature:

  • Disabling notifications in app settings does not help — the app froze on emitting a tool permission request (Emitted tool permission request … for AskUserQuestion … was the last main-process log line), so the blocking call fires for permission-request notifications too.
  • Disabling notifications OS-wide (Windows Settings → System → Notifications, master toggle off) does not help either — the app still makes the synchronous COM round-trip into the notification platform; Windows merely wouldn't display the toast.

Following the chain one level deeper both times: WpnUserService was itself stuck in a COM call into a taskhostw.exe-hosted server. The wedged taskhostw instances' creation times exactly matched the last-run times of the \Microsoft\Windows\WDI\ResolutionHost scheduled task (Windows Diagnostic Infrastructure) — a hung WDI diagnostic module was the root each time. I've disabled that task locally as a machine-side mitigation, but any hung COM server downstream of the notification platform reproduces the app freeze.

Key findings for whoever fixes this

  • Killing/restarting the wedged WpnUserService does not recover the app: the orphaned COM call is never cancelled (WCT afterwards shows the wait target as the dead PID, status PidOnly); only an app relaunch clears it.
  • Killing the root blocker (the wedged taskhostw) made the chain unwind and the app resumed on its own without a restart — confirming the app is healthy and merely parked on the unbounded synchronous call.

Suggested fix

Don't make an unbounded synchronous notification call on the Electron main thread. Fire the completion toast asynchronously (worker thread and/or timeout-guarded) and treat notification-platform failure as non-fatal. A hung OS notification service should cost the user a missing toast, not a permanently dead app — same remedy as the macOS fix in #57706.

Workaround

  • No notification setting helps — neither the in-app toggle nor disabling notifications OS-wide stops the app from making the blocking call.
  • Once frozen: identify the root blocker via Wait Chain Traversal and kill it (app then resumes), or restart the app. (Restarting WpnUserService alone does not unstick it.)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗