send_message (cross-session) never delivers and leaves the target session hung on an empty turn

Status Open
Reported on v2.1.227
Maintainer reply None cached
Activity 4 comments · opened Aug 13, 2026

Summary

mcp__ccd_session_mgmt__send_message (session → session) returns success, renders the message in the target session's UI, and puts that session into a running state — but no turn is ever created. The target spins indefinitely and has to be cancelled with Esc. The target's model never receives the message.

The failure is silent at both ends: the sender gets Message sent to session … ("<title>"), and the recipient looks busy.

Environment

  • Claude Code 2.1.227, desktop app
  • Windows 11 Pro 26200

Reproduction

  1. Open two sessions, A and B.
  2. From A, call send_message targeting B, with a unique token in the body.
  3. Observe B: the message text renders and the working indicator starts.
  4. Wait. B produces no output and stays "working" indefinitely (2+ minutes observed).
  5. Press Esc in B to recover. B returns to a normal prompt undamaged.

Expected: the message arrives in B as a user turn and B acts on it.

Actual: B renders the text, enters a working state, produces nothing, writes nothing, and hangs until interrupted.

Evidence

Checked against the transcript files at ~/.claude/projects/<slug>/<sessionId>.jsonl:

  • The unique token appears in the sender's transcript (the tool call and its result) and in the recipient's.
  • The recipient's transcript is byte-identical before and after the send (93,037 bytes), including after the Esc — the aborted turn leaves no record at all.
  • The recipient's process showed ~6s cumulative CPU while displaying "working" — blocked, not computing.
  • Scanning all 476 transcript files on this machine, the only origin.kind values that have ever existed are:

| origin.kind | count | source |
|---|---|---|
| human | 13,158 | user input |
| task-notification | 965 | background task completion |
| coordinator | 9 | parent → subagent (isSidechain: true, has agentId) |

There is no record kind for a cross-session message. Not one instance, across every session ever run on this machine.

This suggests the wake/notify half of the receive path is wired but the turn-creation half is not, so the recipient is started on a prompt that never materialises.

Impact

Pointing this at a session that is doing real work stalls it until a human notices the spinner and cancels. Because both ends report success and no error surfaces anywhere, there is nothing to indicate the message was lost or that the target is now stuck.

Note

coordinator → subagent messaging (the Agent tool + SendMessage, within a single session) works correctly. Only session → session is affected.

View original on GitHub ↗

3 Comments

arthurmoraesfernandes-afk · 16 days ago

Hung empty turn after a "successful" send — #86298 documents the same failure chain with root-cause analysis (Windows desktop, regression at app 1.28929.0 / bundled CLI 2.1.227, still present on app 1.30096.1.0 / CLI 2.1.229): the desktop bridge sends the message with a class-less origin:{kind:'peer'}, the recipient CLI/SDK's cross-session consent gate holds it for an approval no desktop surface offers, and dialogExpiry (~5 min) then drops it — while the sender keeps a success receipt. A memory-dump repro in that thread shows the payload reaching the recipient CLI process but never its transcript. Setting crossSessionInbound: "accept" in user settings does not unblock it (tested on running, cold-started, and post-restart fresh sessions).

lj2026-web · 16 days ago

Also reproducible on macOS — this is cross-platform, and on this machine it is a regression that landed between Aug 10 and Aug 14. (Both this issue and #86603 are labeled platform:windows; macOS is affected too.)

Environment: Claude Code 2.1.228, desktop app, macOS 26.5.2 (arm64).

Today's failures, all silent-success at the sender:

  • Two supervision handoffs, session A → idle session B: sender got Message sent to session …; the target's UI and list_events rendered the message at the end of its transcript; the target's ~/.claude/projects/<slug>/<uuid>.jsonl never received it (0 hits for the body, no queue-operation record). On one send the target visibly woke — lastActivityAt advanced and it showed "running" for ~14 minutes — but its transcript grew by zero events, matching the wake-without-turn behavior described here.
  • One reply, session B → session A: same loss.
  • One controlled probe with a unique marker (DELIVERY_PROBE_1786712444): the marker appears only in the sender's own transcript (tool_use + tool_result records). Target jsonl: 0 hits. A full binary scan of ~/Library/Application Support/Claude (UTF-8 and UTF-16LE patterns) finds the marker nowhere on disk — the rendered message exists only in app memory/cloud state.
  • Resuming the target via claude --resume and asking it to check its input history: it confirms the marker never appeared in any turn it received. 4/4 lost, both directions.

Two data points that may help triage:

  1. Regression window on macOS: between 2026-08-10 and 2026-08-14. Transcripts on this machine from Aug 9–10 show the working shape — {"type":"queue-operation","operation":"enqueue",…} carrying the <cross-session-message …> envelope in plaintext, immediately followed by the same content as a user record. In today's failures that enqueue record is simply never written, while the wake still happens — consistent with #86603's finding that the notify half survives and the enqueue/persist half fails. The window also lines up with #86603's "worked on this machine until 2026-08-11" on Windows.
  1. The rendering layer actively misleads. list_events (the ccd_session_mgmt MCP read tool) renders the lost message at the end of the target's transcript, so a sender that double-checks "did it arrive?" sees it there and reasonably concludes delivery succeeded. Cross-session orchestration (supervision / handoff flows) fails silently at both ends: sender sees success plus the rendered message, target sees nothing, and no error surfaces anywhere.

Workarounds we have adopted: treat send_message as a UI notice only; anything the target must actually receive goes through claude --resume <target-cli-session-id> (message-as-turn-input, 100% arrival) or a durable file both sides read.

DUCKJAIII · 14 days ago

Same failure on Windows 11, desktop app, 2.1.229 — with one difference from your title that may matter for diagnosis.

The target session did not hang. It carried on completely normally after both sends: no spinner, no empty turn, nothing to cancel. It ran 60+ further turns on real user input and finished a long workflow cleanly. As far as it was concerned the messages never existed.

So the silent drop occurs without the hang. Those look like separable failures rather than one, which would also explain why #86059 and #86386 describe the recipient stopping or spinning while this case shows neither.

A second success-shaped return. You quote Message sent to session … ("<title>"), which is what I got when the target was idle. There is another when the target has a turn in flight:

Message queued for session <id> ("<title>"); it will be processed after the in-flight turn finishes if that session stays healthy.

Also never delivered. That one is at least conditional, but the condition is unfalsifiable from the sender's side — and the target here demonstrably stayed healthy.

Evidence, using your method

Two messages A → B, ~6 min apart, each with a unique token. grep -c on the on-disk transcripts:

| token | sender | recipient |
|---|---|---|
| A | 6 | 0 |
| B | 6 | 0 |
| cross-session-message | — | 0 |

Recipient identity confirmed independently before counting; 2,238,259 bytes and actively written throughout, so this is not a stalled or wrong file.

Neither did list_events or search_session_transcripts show them — I verified the latter does index that session by first searching a string I knew was in it. (Note the macOS report #86370 states the message is visible via list_events there; on Windows it was not, though I paged ~70 of 977 events rather than all.)

Detection, not a workaround

There is no way to make the message arrive. The only option is to notice it didn't: after sending, search search_session_transcripts for a distinctive string from the body, and treat the return value as carrying no information either way.

Showing cached comments. Read the full discussion on GitHub ↗