[BUG] Windows Desktop 2.1.227: cross-session send_message to a paused session is never delivered to the model, leaving the session in a permanent phantom turn
Summary
On the Windows Claude Desktop app, mcp__ccd_session_mgmt__send_message stopped delivering messages to paused (idle-timed-out) sessions after the desktop app switched its bundled Claude Code binary to 2.1.227.
The tool returns Message sent, the target session is resumed, and the message renders in the target session's UI — but the model never receives it. No tokens, no thinking output, the elapsed-time counter runs indefinitely. On restart the message is gone from the transcript entirely.
The failed delivery also leaves the target session in a turn that never completes. Every subsequent send_message to that session returns Message queued ...; it will be processed after the in-flight turn finishes and queues behind a turn that will never finish. Only a manual interrupt in the UI clears it.
Environment
- OS: Windows 10 (19045)
- Claude desktop app: 1.28929.0.0
- Bundled Claude Code binary: 2.1.227 (
%APPDATA%\Claude\claude-code\2.1.227\claude.exe) - Last known-good binary: 2.1.222 (still present in the same directory)
Steps to reproduce
- Let session B sit idle until
WarmLifecycledisconnects it (900s idle timeout). - From session A, call
mcp__ccd_session_mgmt__send_messagetargeting B. - The tool returns
Message sent to session <id>. Session B shows the message in its UI with a running counter. - Session B never processes it — no tokens consumed, no thinking output.
- Send a second message to B. The tool now returns
Message queued ...; it will be processed after the in-flight turn finishes. That one is never processed either. - Restart the desktop app. The message is gone from B's transcript.
Log evidence
Successful delivery (target already warm) — main.log writes a delivery line:
17:58:52 [info] Sending message to session local_be70b723-...
17:59:25 [info] [CCD CycleHealth] healthy cycle for local_be70b723-... (33s, hadFirstResponse=true)
Failed delivery (target paused) — the session is resumed, but no delivery line is ever written and it goes straight back to idle:
18:00:46 [info] Resuming session local_b0d81a0c-... in <workspace>
18:00:46 [info] Starting local session local_b0d81a0c-...
18:00:46 [info] Loaded 206 transcript messages for session local_b0d81a0c-...
18:01:05 [info] [WarmLifecycle:session] Starting idle timeout for local_b0d81a0c-...: 900s
The phantom turn surfaces in CycleHealth once the session is interrupted or the app quits:
17:56:13 [info] [CCD CycleHealth] healthy cycle for local_b0d81a0c-... (336s, hadFirstResponse=false)
17:59:47 [info] [CCD CycleHealth] unhealthy cycle for local_d0cf62bd-... (974s, hadFirstResponse=false, reason=no_response)
Regression window
Pairing every Resuming session <id> with the next CycleHealth line for the same id, within a single log file spanning both binaries:
| Time | Resume → first turn |
|---|---|
| Aug 11, all day → Aug 12, 08:24 | processed in every case (hadFirstResponse=true) |
| Aug 12, 09:31 | desktop app downloads and switches to binary 2.1.227 |
| Aug 12, 10:08 onward | send_message-triggered resumes never processed (hadFirstResponse=false) |
A full restart of the desktop app does not help — the failure reproduces on the first message after restart.
Expected behavior
A message sent to a paused session should reach the model once the session is resumed, exactly as it does for a warm session. Failing that, the tool should return a delivery error instead of Message sent, and it should not leave the target session in a turn that never completes.
Impact
This is the transport for multi-session coordination. Handoffs between sessions vanish silently: the sender sees success, the recipient's UI shows the text, and nobody notices until the work simply doesn't happen. The queueing behavior compounds it — the first lost message poisons that session for every message after it.
Workaround
None that is reliable. Manually opening and interrupting the target session clears the phantom turn, and messages to a session that is already warm and idle are delivered normally.
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Correction to the Workaround section, plus one more symptom
The workaround I described does not actually work: interrupting the stuck session from the UI does not clear the phantom turn. The session shows
Stopping…and stays there indefinitely — the interrupt is never honoured.The only reliable recovery I have found is killing that session's
claude.exechild process (identifiable by its--resume=<cli-session-id>argument, whichmain.logmaps to the internal session id viaMapping internal session <local_id> to CLI session <cli_id>), or restarting the desktop app. Restarting does not prevent the bug — it reproduces on the very first message sent to a paused session afterwards.One more detail that may help narrow down where the message is dropped: the message text does render in the target session's UI while the counter runs, but it is gone after a restart. So the frontend receives and displays it, while nothing reaches the session transcript or the model — the loss appears to sit between the UI layer and the session's turn queue.
looks very similar to #86014
possible dupe of #86012
Likely the same root cause as #86012 (and #86014 from the sender's side) — happy for this to be closed as a duplicate if maintainers agree.
Two things here that I did not see in those reports, in case they are useful for the fix:
Resuming session <id>with the nextCycleHealthline for the same id in one log file: every resumed turn was processed through Aug 12 08:24, the desktop app switched to binary 2.1.227 at 09:31, and from 10:08 onward nosend_message-triggered resume was ever processed.main.logwritesSending message to session <id>. For a failed one that line is never written — the session is resumed, the transcript is loaded, and it goes straight back to idle.Also worth correcting my own report: I initially thought this only affected paused sessions. #86012 is right that the target's state does not matter — a message to an already-warm session was written to the log as delivered and still produced no output.