You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Windows desktop app: cross-session send_message silently fails (reports success, never delivered) after runtime 2.1.222→2.1.227 auto-update #86275
Cross-session messaging between local sessions in the Claude Code desktop app on native Windows worked reliably before the app auto-updated its bundled runtime (2.1.222 → 2.1.227 on 2026-08-12). After the update, sending a message to another local session reports success but the message is never delivered — a silent failure with a false success signal.
Environment
OS: Windows 11 Pro (build 10.0.26200), native (not WSL)
Claude Code desktop app (bundled runtime auto-updated 2.1.222 → 2.1.227 on 2026-08-12)
Separately installed CLI: 2.1.220 (not involved in repro)
Multiple concurrent local sessions in the desktop app, same machine, same OS user
What worked before (≤ 2.1.222, desktop app)
We ran 5+ concurrent sessions coordinating a project. Session-to-session messages (the desktop app's session-management send_message) were delivered both directions for days.
What happens after the update (2.1.227)
Session A sends a message to session B via the desktop app's session messaging.
The tool returns success: Message sent to session local_… ("<title>").
Nothing ever appears in session B — confirmed by a human watching session B (not just by the sender).
Immediately after the update there was an intermediate state where A→B deliveries still arrived but replies B→A were silently lost; after a reboot, deliveries stopped entirely while the sender still gets a success result.
No error, no held-message notice, no expiry notice on either side.
Expected
Either of:
messages are delivered as before, or
the send call fails loudly on an unsupported platform instead of returning success.
The docs (code.claude.com/docs/en/cross-session-messaging) state the CLI cross-session messaging feature (v2.1.224+) runs on macOS/Linux only and is not offered on native Windows. If the desktop app's session messaging on Windows was intentionally gated off by the same rollout, the regression is still the false success response — automation built on the sender's success signal breaks invisibly. A platform-unsupported error (or a "held/refused" outcome per the documented delivery model) would let clients fall back cleanly.
Impact
We coordinated a multi-session workflow (coordinator + 5 team sessions) on this channel; after the silent breakage we lost most of a morning diagnosing sessions/user error before isolating the app update as the trigger, and had to migrate coordination to file-based message passing.
Repro
Windows 11, desktop app with runtime ≥ 2.1.224 (observed on 2.1.227).
Open two local sessions.
From session A, ask Claude to send a message to session B (session-management send_message).
Observe: sender gets "Message sent…", session B never receives it.
Happy to provide timestamps or additional logs if useful.
Likely related / same regression family: #86012 (same environment — Windows 11 10.0.26200, desktop app, bundled CLI 2.1.227; recipient session hangs with hadFirstResponse=false after send_message injection). #86012's mechanism explains what we observed as "silent non-delivery": the recipient query stalls entirely, so from the sender's side the message appears sent-but-never-answered. Our report adds the false-success axis: the sender receives "Message sent" even when nothing is ever processed.
Reacted by aa09358743, x123456789172-sys, timeisnotavailable, Carola Kermer and Vadim Nikitushkin
Reproduced independently on a second Windows 11 Pro machine (build 10.0.26200, desktop app, native, multiple local sessions, same OS user).
One additional data point that narrows it down: in our repro the message does render in the target session's transcript UI (a "Message from {sender}" card is visible to the human), but it never enters the model's context — when asked, the target session enumerates its received turns and the message is not among them, and no new turn is spawned. So delivery to the UI succeeds; injection into the model's conversation / turn-spawning is what fails. Possibly related to #61718 (queued messages delivered but not actioned).
Reacted by x123456789172-sys, timeisnotavailable, Vadim Nikitushkin and aa09358743
Second independent report — different machine, different account, Windows 10.
We hit this on 2026-08-12/13 and spent about five hours misdiagnosing it before
finding this issue. Adding only what isn't already covered above.
Environment (differs from OP)
Windows 10 Pro 19045 (OP is Windows 11 Pro) — so it is not 11-specific
Claude desktop app 1.28929.0.0 (MSIX/Appx), model Opus 5
Multiple concurrent sessions, several in git worktrees
Reproduced by a second operator on separate hardware with a different account —
same symptom, so it is not per-machine or per-account state
Objective check that doesn't rely on asking the model
A successfully injected message appears in the recipient's transcript JSONL as a user turn wrapping a <cross-session-message …> block. When this bug occurs, that turn is simply absent — the transcript is byte-identical to one where no
message was ever sent.
This is a cleaner signal than asking the receiving session, because it separates
"the model didn't mention it" from "the model never had it":
recipient transcript: 76 genuine <cross-session-message> user turns
last one: 2026-08-12 14:05 UTC
every message after that: absent, while all returned success at the send layer
Restarting does not help
App restart ×2 — no change
Full machine reboot — no change
Worth stating explicitly, since a false-success bug often reads like stale
process state. It isn't.
Failure is bidirectional
Not just "session A can't reach session B". Verified in both directions between
the same pair within minutes of each other — each side's message card rendered in
the other's window, and neither model received anything.
queued vs sent is a red herring
When the target is mid-turn the tool returns:
Message queued for session <id>; it will be processed after the in-flight turn
finishes if that session stays healthy.
We initially believed this was the whole story (one of our sessions was genuinely
stuck in a non-terminating turn — Esc and Ctrl+C both ineffective). It is not:
messages returning plain sent to demonstrably idle sessions were lost identically.
Anyone else debugging this will probably go down that path too.
On the "return an error instead of success" request
Strongly seconding this, and it matters beyond this particular regression.
Three layers report independently, and only the last one is load-bearing:
Layer
Signal
Actual
Send
Message sent to session …
reports success
UI
Message card rendered in recipient window
visibly present
Context injection
no signal at all
never happens
Because the two observable layers both say "fine", the human operator watching the
recipient's window sees the message arrive and reasonably concludes it was delivered.
That is what cost us the five hours — every intermediate hypothesis we formed
(dead session id, recipient stuck mid-turn, transient app state) was fully consistent
with everything we could observe.
Even after this regression is fixed, a success string that cannot distinguish
"handed to the UI" from "injected into the recipient's context" will make the next
delivery failure equally silent.
Workaround (same as OP, confirming it works)
Coordination moved to the shared git repo: write the message as a file, commit, push;
the recipient runs git pull and reads it. Slower, but delivery is verifiable —
the file either exists at a known commit or it does not.
Reacted by aa09358743, timeisnotavailable, Carola Kermer, Nima Pirmoradian and Vadim Nikitushkin
Confirming this regression on Windows Desktop, with logs.
Versions
Desktop app: 1.28929.0.0 (MSIX / Windows Store), auto-updated from 1.26832.0
Bundled CLI runtime: 2.1.227
Update happened 2026-08-13 11:00 local time; the delivery failure starts exactly there.
Symptom send_message returns success to the sender ("Message queued for session local_…",
and in a later attempt "Message sent to session local_…"), but the target session
never receives anything. No error, no retry, nothing in the target's queue.
The message IS rendered in the target session's UI window, so the user sees it
while the model never does — the assistant truthfully answers "no task arrived"
and looks like it is ignoring instructions. That mismatch is what makes this
expensive: we lost ~2 hours across three sessions before suspecting the tool.
Evidence
Last successful delivery: 2026-08-12 13:57 local (dozens of deliveries worked fine
that day and the day before, including one after a 10-hour idle gap).
After the update: zero deliveries across three freshly created sessions.
Each target's transcript queue contains only messages typed by the human:
grep '"operation":"enqueue"' .jsonl
→ only human-typed entries, no <cross-session-message …> entries
Controlled test on a completely fresh system:
14:34:26 Windows reboot (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
14:36:48 [updater] app start
14:37:03 new session created (target)
14:37:45 main.log: "Sending message to session local_"
→ target queue still contains only its own opening message. Not delivered.
So the app-side dispatch is logged, but the message never reaches the CLI session.
Related warning seen earlier in main.log for another target session:
[warn] [LocalSessionManager] reaped 1 stale pendingEchoUuids for local_
— CLI dropped the isReplay echo
Ruled out
Wrong session id (verified: get_session on the id rejects it as "the current session")
Idle timeout (deliveries worked after a 10h idle gap pre-update; and a target that
was demonstrably awake, actively chatting with the user, also received nothing)
Message length (a 4-line message failed just like long ones)
Anthropic service incident (status page clean)
Stale process/queue state (full OS reboot does not fix it)
Impact
Any multi-session workflow that hands work between a planning session and an
execution session is silently broken, and the failure mode actively misleads the
user: the sender is told it worked, the human sees the message in the UI, only the
receiving model never gets it.
Suggested minimum fix
Make the success return value depend on the message actually being enqueued in the
target session, and surface a hard error otherwise — same direction as the 2.1.224
fix, which this regression appears to undo.
Confirming this on Windows with additional data points, including one that may help narrow the cause: send_message returns success even when the target session ID doesn't exist.
Environment
Claude Desktop 1.28929.0 (d1a6bc), 2026-08-11 build, Windows Server VM
32 GB RAM / 8 logical processors (AMD EPYC 9274F host) — failures occur at ~6% CPU / 14% RAM, so not resource-related
2–3 concurrent Code-tab sessions, same cwd, communicating via ccd_session_mgmt (a Reviewer/Doer code-review workflow that ran ~80 work units cleanly before this regression)
Opus 5, high effort
Loss rate: In one session, the sending agent tracked its own sends: 3 of 4 lost — send returned Message sent (or queued), recipient's transcript (checked directly by the recipient session enumerating its own history) contained nothing. The two load-bearing sends (a work plan and a plan-request) were among the lost.
No addressee validation: A send to a non-existent session ID also returned sent. One of our sessions had inferred its own ID from its transcript-file UUID (which differs from the session-management ID — list_sessions excludes self, so self-ID is guessable-wrong); peers sending to that wrong ID got success results. So sent appears to mean "the call returned," not "a recipient exists," which may be the same underlying cause as silent drops to valid-but-idle sessions.
Correlated hangs: Sessions composing large multi-paragraph send_message payloads (plans, reviews) wedge mid-turn — spinner active, no output, input queues, 8–28 minutes until "Claude Code stopped responding." Two Doer sessions wedged simultaneously, both mid-send. Short sends mostly survive. This matches #86012's recipient-side description; we see it on the sender side too.
Timeline fits the regression window: this workflow was reliable until roughly the 1.28929.0 / runtime 2.1.22x update period. Happy to provide AppData logs from ~12 incidents across Aug 12–13 if useful.
Confirming: 2.1.228 does NOT fix this. Clean A/B test below.
Another Windows/MSIX data point, but the new part is the version: I upgraded the CLI to the official 2.1.228 and the failure reproduces exactly. Reverting to 2.1.222 restores delivery within the same minute.
Clean A/B test — only variable changed is the CLI binary
Same target session, same session state (restored after app restart, no activity since), same machine, same sender, same message shape. Nothing else touched between the two runs.
I also tested two other targets under 2.1.228 — one with a 4.5 MB context and one freshly created after the upgrade and active seconds before the send. All three: zero bytes of growth. So it is not "the receiver is just slow" and not "only restored sessions are affected" (the freshly created one failed too).
Important diagnostic difference: the 2.1.227 log marker does not appear on 2.1.228
On 2.1.227 the failure is accompanied by:
[warn] [LocalSessionManager] reaped N stale pendingEchoUuids for local_xxx — CLI dropped the isReplay echo
On 2.1.228 this string never appears. In my main.log it stayed at 22 occurrences, last one 2026-08-13 21:32:49, i.e. zero new occurrences under 2.1.228 while delivery was fully broken.
Instead, under 2.1.228 there is no delivery activity logged at all. Under 2.1.222 every send produces:
[info] [LocalSessionManager] drained N deferred send(s) for local_xxx
[info] [LocalSessionManager] isRunning held by unechoed input at result for local_xxx
Under 2.1.228, nothing — not even an attempt.
⚠️Consequence for anyone triaging this:CLI dropped the isReplay echo is a permanently negative health indicator on 2.1.228. If you use it to decide whether the channel is healthy, you will always get "fine". 2.1.227 and 2.1.228 appear to fail at different points: 227 drops the echo, 228 never attempts delivery.
Both hashes verified before and after each swap. After the upgrade, the version field inside my own session .jsonl read 2.1.228 — so the process really was running the new binary, not a stale one.
Environment
Claude Desktop 1.28929.0.0 (MSIX / Store install), Windows 11 Pro 26200
[CCD-autoupdate] Disabled: MSIX install on every launch — the CLI version is pinned by the desktop app, it does not self-update
Because the desktop app only loads the CLI from the version directory it expects, 2.1.228 was placed into that directory (same workaround as the OP used for 2.1.222)
The only landing criterion I found trustworthy
Four signals all report success while nothing is delivered:
send_message return value (sentandqueued both lie)
list_events — the message is visible in the UI store
drained N deferred send(s) in main.log
flushed held steers counts
The only reliable check is the receiver's transcript on disk:
a record with type == "user", notoolUseResult, body containing the cross-session message opening tag, and a timestamp later than the send.
⚠️One trap that will make you misread a healthy channel as broken: an in-flight turn is not flushed to disk. The user record that triggered a turn is only written after that turn ends. So the receiver can never observe its own inbound record within the turn that is processing it — ask a third party to read the receiver's transcript instead, or wait for the turn to finish. I lost time to this before realising the channel was fine at that moment.
Suggested triage note
Given 222 works and both 227 and 228 fail — and the issue body states the feature was introduced in 2.1.224+ — it would help a lot to know whether 2.1.224 / 225 / 226 are affected, and whether the fix shipped in 2.1.228 (described as applying to "the first session after install or upgrade") was ever intended to cover the case here, which is all sessions, indefinitely, not just the first one after upgrade.
Happy to run further tests on this machine — I have both binaries staged and can swap them in a couple of minutes.
Update after upgrading to desktop 1.30096.0.0 / runtime 2.1.229: the false-success axis reported here appears fixed — sends now deliver and render in the recipient session (2.1.228 changelog fix confirmed working). However the recipient session then hangs with zero output (#86012), reproduced on 2.1.229. Closing the loop: our original 'silent non-delivery' was likely #86012's hang observed from the sender side. This issue's remaining value is the platform question (docs still say native Windows unsupported, yet delivery now works there).
arthurmoraesfernandes-afk commented on Aug 14, 2026
Same regression window and signature — #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).
Independent confirmation on a second Windows machine, with the boundary measured to the minute. Same platform and install type: Windows 11, native (not WSL), Claude Code desktop app with its bundled/managed runtime.
Same boundary, measured from this machine's own logs
this machine
this issue
last working managed runtime
2.1.222
2.1.222
first broken
2.1.227 (then 2.1.229)
2.1.224+ (seen on 2.1.227)
runtime changeover
2026-08-12 19:28 local (Europe/Amsterdam)
2026-08-12
separately installed CLI
not involved
not involved
The desktop app's own version (app-1.26832.0 to 1.28929.0) moved in the same step. Worth noting for anyone bisecting: 1.28929.0 was staged at 01:55 on 08-12 but did not become the running instance until 19:27 (main.log logs the earlier start and then "Not main instance, returning early"), so the app-version timestamp alone is misleading.
Last successful cross-session delivery here: 2026-08-12T14:52:43Z, about 2h35m before the runtime changeover. Scanning all 1,068 session transcripts under ~/.claude/projects, restricted to top-level type:"user" records whose content begins with <cross-session-message, gives 582 delivered peer turns, and none after that timestamp.
The failure signature, from the app's own watchdog
This may be the most useful part. When the desktop app reaps a wedged session it writes a post-mortem to %APPDATA%\Claude\logs\main.log. For every target of a failed peer send:
timed out after 1001s of inactivity (hadFirstResponse=false, last_message_type=user,
last_tool_name=none, seconds_since_stderr=never)
(1001s / 1008s / 1015s / 964s / 771s across the batch, all hadFirstResponse=false.)
seconds_since_stderr=never plus zero stderr tail lines means the spawned managed runtime lived about 16 minutes and emitted zero bytes on stdout AND stderr. It never even sent system/init. The same binary, reached through the same code path by an ordinary user-typed message to the same session, initialises in about 4 seconds and answers normally.
So the wedge follows the record, not the spawn: a record carrying origin.kind:"peer" never becomes a turn, warm or cold, new chat or old.
On the sending side, Sending message to session ... is logged and Message sent is returned, but the enqueue is fire-and-forget. Nothing in the path awaits an init or a turn echo, so success is returned unconditionally. That is why nothing surfaces to the user until the roughly 16-minute reaper fires.
Ruled out by test, not by inference
"crossSessionInbound": "accept" in user settings plus a full app restart: no change, on both an existing chat and a brand-new chat created after the setting.
Target liveness and peer-registry registration: no change. Targets were registered, running and warmed.
The four documented flag-evaluation variables (CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, DISABLE_TELEMETRY, DO_NOT_TRACK, DISABLE_GROWTHBOOK): not set at User, Machine or Process scope. A full sweep of both persistent scopes finds no related variable.
Local hooks and config of our own: none touch messaging.
One data point for #86298 specifically, since it proposes a hold-for-approval that expires: setting crossSessionInbound: "accept" here, which by the documented precedence should remove any default hold, changed nothing on either an existing or a freshly created chat. And on this machine the death clock is about 1000s, not about 5 min, and it comes from the app's inactivity reaper rather than a dialog expiry. So if the hold hypothesis is right, accept is not being honoured on this path.
Why this one hurts: three weeks of daily use, then silence
We were not experimenting with this. We had been using it in production for three weeks. Two Claude Code sessions coordinated an autonomous test loop on one machine, handing work back and forth, and cross-session messaging carried it: 582 delivered messages before it stopped. Whether the capability was ever formally supported on Windows or not, it was discoverable, it reported success, and it worked reliably every day, so we built on it.
That is what makes the silent failure costly. Nothing announced a change. The API kept returning Message sent. Both sides kept behaving as if delivery had happened, and the only signal was work quietly not getting done. We spent a day diagnosing our own configuration before finding this issue.
Request
Please could this be prioritised: restore delivery on Windows if the platform is meant to be supported, or, if it genuinely is not, make the send fail loudly instead of returning success. Either is workable. The present state, where the call reports success and nothing is delivered, is the one that cannot be designed around.
A one-line release note would also have saved a lot of time here.
Happy to supply the full main.log extracts, transcript scans or the reaper post-mortems if any of it is useful.
(For anyone blocked meanwhile: we fell back to passing the payload through a file on disk and using the message only as a doorbell, the same conclusion as the reporter.)
Addendum to my comment above. The public changelog answers the "was this ever supported" question that I left open, and it lines up exactly with the boundary measured on my machine.
2.1.224 (Aug 7) is where the feature was introduced, and Windows was never in scope
Added cross-session SendMessage: Claude Code sessions can now message each other, on any of your machines, with ListAgents to discover them (macOS and Linux)
Added crossSessionInbound and dialogExpiry settings: cross-session messages sent to a session running with bypassed permissions are held for your approval, and messages to other sessions auto-deliver
Set against the runtime versions on this machine:
last working managed runtime 2.1.222
current managed runtime 2.1.229
in between 2.1.224
So the likely shape is not "a working Windows feature regressed". It is that before 2.1.224 there was no runtime-level cross-session messaging at all, and the desktop app's own delivery path worked on Windows by default. From 2.1.224 the runtime owns peer messaging, scoped to macOS and Linux. My machine crossed that boundary in a single jump and the capability changed owner underneath it.
That reading fits every negative result I reported above: no setting helps, a freshly created chat does not help, target liveness does not help, and the spawned runtime takes the record and emits nothing on either pipe.
Two more changelog entries that bear directly on this
2.1.224, same release:
Fixed SendMessage reporting "Message sent" when the write to a teammate's inbox had actually failed; failed deliveries are now reported as errors
The identical false-success problem was recognised and fixed for the teammate path in the very release that introduced cross-session sending. The cross-session path on Windows still returns success on a delivery that never happens.
2.1.225:
Fixed cross-session messages staying parked without a notice or expiry in headless sessions and during startup
"Staying parked without a notice or expiry" is the exact symptom here, fixed for two other contexts.
A note for anyone advising a settings change
2.1.232 added the /config rows for "Dialog expiry" and "Messages from your other sessions" (cross-session inbound accept/hold/refuse). The desktop app pins its own bundled runtime: mine is 2.1.229, while the CLI changelog is already at 2.1.233. So those /config rows do not exist for desktop users on this runtime, and neither does any UI for the inbound setting. Writing crossSessionInbound into settings.json by hand is possible, and as reported above it changed nothing here.
This also means desktop users cannot reach a CLI fix by updating anything themselves. The bundled runtime moves only when the desktop app bumps it.
The request, restated more precisely
Given the above, this is less "please fix a regression" and more:
Please bring cross-session messaging to Windows on the desktop app, or say plainly that it will not come, so people can plan.
Until then, please make the send fail loudly on an unsupported platform, the same way the teammate-inbox failure was made loud in 2.1.224. A call that returns Message sent on a platform where delivery is out of scope is the part that cost us a day and three weeks of a working workflow.
A line in the changelog when a capability changes platform scope would have made this self-diagnosing.
Happy to provide the runtime version timeline, the app watchdog post-mortems, or the transcript scans from my earlier comment if any of it helps.
Addendum to my comment above. The public changelog answers the "was this ever supported" question that I left open, and it lines up exactly with the boundary measured on my machine.
2.1.224 (Aug 7) is where the feature was introduced
Added cross-session SendMessage: Claude Code sessions can now message each other, on any of your machines, with ListAgents to discover them (macOS and Linux)
Added crossSessionInbound and dialogExpiry settings: cross-session messages sent to a session running with bypassed permissions are held for your approval, and messages to other sessions auto-deliver
Set against the runtime versions on this machine:
last working managed runtime 2.1.222
current managed runtime 2.1.229
in between 2.1.224
One reading is that before 2.1.224 there was no runtime-level cross-session messaging at all, the desktop app's own delivery path worked on Windows, and from 2.1.224 the runtime owns peer messaging with macOS and Linux in scope. My machine crossed that boundary in a single jump.
I want to flag the counter-evidence rather than push that reading too hard, because #86629 carries the regression label from triage, and its reporter measured delivery working on 2.1.219, 2.1.221 and 2.1.222 with 74 calls cross-checked. If this were purely a documented platform scope being enforced, regression would be the wrong label. So the honest position is: the boundary is certain, the intent behind it is not.
Two more changelog entries that bear directly on this
2.1.224, same release:
Fixed SendMessage reporting "Message sent" when the write to a teammate's inbox had actually failed; failed deliveries are now reported as errors
The identical false-success problem was recognised and fixed for the teammate path in the very release that introduced cross-session sending. The cross-session path on Windows still returns success on a delivery that never happens.
2.1.225:
Fixed cross-session messages staying parked without a notice or expiry in headless sessions and during startup
"Staying parked without a notice or expiry" is the exact symptom here, fixed for two other contexts.
A note for anyone advising a settings change
2.1.232 added the /config rows for "Dialog expiry" and "Messages from your other sessions" (cross-session inbound accept/hold/refuse). The desktop app pins its own bundled runtime: mine is 2.1.229, while the CLI changelog is already at 2.1.233. So those /config rows do not exist for desktop users on this runtime. Writing crossSessionInbound into settings.json by hand is possible, and as reported above it changed nothing here.
And a desktop-app update does not necessarily move the runtime. Mine updated to 1.30096.5 on 2026-08-15 and delivery still fails, because %APPDATA%\Claude\claude-code\ still contains only 2.1.229. Anyone re-testing after an app update should check that folder first, or they are re-testing the same runtime.
The request, restated more precisely
Please make cross-session messaging work on Windows on the desktop app, or state plainly that it will not, so people can plan.
Until then, please make the send fail loudly, the same way the teammate-inbox failure was made loud in 2.1.224. A call that returns Message sent when delivery cannot happen is the part that cost us a day and three weeks of a working workflow.
A changelog line when a capability changes platform scope would have made this self-diagnosing.
Happy to provide the runtime version timeline, the app watchdog post-mortems, or the transcript scans from my earlier comment if any of it helps.
Cross-session send_message is silently lost: message is persisted and rendered, but never enters the recipient's model context
Related to #86275. On runtime 2.1.232 the symptom described there ("reports success but never arrives") still reproduces, but the actual mechanism is narrower and more dangerous than the original report suggests — and it defeats the obvious ways of verifying delivery.
Environment
Runtime
2.1.232
App
Claude Code, Windows desktop
OS
Windows 11 Pro 10.0.26200
Model
Opus 5 (claude-opus-5)
Two local sessions in the same repo:
A — local_38bdcd6f-…, branch claude/send-message-cross-session-test-095e08, running in a worktree.
B — the recipient, cwd C:\projects\tim-company. Believed to have been created by A via spawn_task, i.e. a background-task session. (See "Unrun control" below — this may be load-bearing.)
What happened
B → A works. B sent B#1. Tool returned:
Message queued for session local_38bdcd6f-… ("Send Message 跨 Session 測試"); it will be processed after the in-flight turn finishes if that session stays healthy.
A received it and quoted it back verbatim. Delivery confirmed.
A → B silently fails. A sent A#1, waited, then sent A#2, and waited ~10 minutes. Both calls returned success on A's side.
On B's side, three layers came apart:
Layer
Result
Written to B's transcript on disk
✅ present
Rendered in B's UI as a message block
✅ visible to the human
Injected into B's model context
❌ absent
B was never woken, and B's model never saw either message.
The worst part. The human then typed into B's window manually. B woke and responded normally — the session was alive. But the two pending messages were still not included in that turn's context. This is not delayed delivery and not a failure to wake: the content is permanently lost to the recipient model.
B's isRunning stayed true while lastActivityAt was frozen at the moment B's task terminated.
Why this is worse than "message not delivered"
Every available way of verifying delivery returns a false positive.
The human sees the message rendered on B's screen and reasonably assumes B can read it.
The only party that knows the message never arrived is the recipient model — and it cannot know, because from its perspective nothing happened. There is no observable signal that content is missing.
For multi-agent / multi-session workflows this means handoffs can fail with no error anywhere in the system, and any post-hoc audit of the transcripts will show the handoff as successful.
Two return strings map to recipient state
Worth noting, since #86275 quotes only the first form:
Recipient state at send time
Return string
Busy (mid-turn)
Message queued for session … it will be processed after the in-flight turn finishes if that session stays healthy.
Idle
Message sent to session ….
Both are reported as success. In our repro the queued form was returned for a target whose turn had already reached a terminal [result] done (success) state — so it was queued behind a turn that had already finished and would never finish again.
Expected vs actual
Expected: either the message enters the recipient's context and triggers a turn, or the send call fails with an error naming the reason.
Actual: the call reports success, the message is persisted and rendered, and the recipient model never sees it — including on subsequent turns triggered by other means.
Steps to reproduce
From session A, create session B via spawn_task.
Let B complete its task and reach [result] done (success).
From A, call mcp__ccd_session_mgmt__send_message targeting B. Observe the success return.
Observe the message appear in B's UI and transcript.
Type into B's window manually to force a turn.
Ask B what messages it has received. B reports none, and has no access to the message content.
Unrun control (please don't over-read our result)
We have not yet run the same test against a manually opened session as the recipient. Our B was (as far as we can tell) a spawn_task-created background session that had reached a terminal state. So this repro may be scoped to "messages to a completed background-task session" rather than the general case in #86275. Both are real bugs; conflating them would be misleading.
Suggested fixes, in priority order
Do not return success for a recipient that cannot process the message. If the target session is in a terminal state, return an explicit error naming the state. Silent success is the root harm here.
Inject pending messages into the next turn regardless of what triggered it. A manual user turn should flush the queue. Currently pending messages are skipped even when a turn does run.
Fix isRunning for terminated task sessions — it stays true with a frozen lastActivityAt, so list_sessions misreports the state that callers would use to decide whether sending is safe.
Document that reading the recipient's transcript does not verify delivery. Transcript presence and context presence are different things; right now nothing communicates that.
Working fix on Windows: swap the desktop app's bundled engine to >= 2.1.232
Cross-session messaging is working again on my Windows machine, verified both directions on disk. Posting the full recipe since nobody in this thread or #86629 / #86069 has had a workaround.
The key point: there are TWO locks and you need both
engine >= 2.1.232 AND "crossSessionInbound": "accept"
Neither alone works. Engines older than 2.1.232 ignore the setting entirely. That is why I reported earlier in this thread that crossSessionInbound: "accept" changed nothing: I was on 2.1.229, so the setting was never read. That negative result of mine was untestable, not a refutation, and I want to correct it explicitly because it may have sent others down the wrong path.
Confirmed on my machine: engine 2.1.234 alone was not tried in isolation here, but on the machine where this recipe was developed, 2.1.234 alone also failed and only the pair worked.
Recipe
1. Check what you have.
Get-ChildItem"$env:APPDATA\Claude\claude-code"-Directory |Select-Object Name
Get-Process claude |Where-Object { $_.Path-like"*claude-code*" } |Select-Object ProductVersion
If the live engine is already >= 2.1.232, skip to step 3.
2. Get a newer engine and swap it in. The npm package ships the same native binary.
npm install -g @anthropic-ai/claude-code@latest&"$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe"--version # expect 2.1.234+$dir="$env:APPDATA\Claude\claude-code\2.1.229"# <- your folder name from step 1$new="$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe"Copy-Item"$dir\.payload""$dir\.payload.bak"Copy-Item"$dir\.verified""$dir\.verified.bak"Rename-Item"$dir\claude.exe""claude.exe.bak"Copy-Item$new"$dir\claude.exe"$h= (Get-FileHash"$dir\claude.exe"-Algorithm SHA256).Hash.ToLower()
$s= (Get-Item"$dir\claude.exe").Length
"{`n`"sha256`": `"$h`",`n`"size`": $s`n}"|Out-File"$dir\.payload"-Encoding ascii
Notes: Windows lets you rename a running exe, so open chats keep working on the old process. Leave .verified alone even though it is now stale; the app accepted it with no error and no re-download. The folder keeps its old name and the process reports the new version.
3. Add the setting to %USERPROFILE%\.claude\settings.json, preserving your existing keys:
{ "crossSessionInbound": "accept" }
4. Quit the app completely, including the tray icon, and reopen.
Verify, and do not trust the usual signals
The sender's Message sent, the card that renders in the target window, and main.log all report success on a delivery that never happened. The only honest check is the receiving session's own transcript:
Success looks like this, in the recipient's jsonl:
"type":"queue-operation","operation":"enqueue","timestamp":"..."
"type":"user" containing <cross-session-message
"type":"assistant" the reply
On my machine: enqueue at +2s, model reply at +24s, and the return direction landed the same way.
Caveats, stated plainly
This is an unofficial local modification of a vendor binary. Fully reversible: delete the new exe, rename the three .bak files back, restart.
An engine auto-update silently undoes it. The folder gets replaced with the official build and messaging dies again with no error. Re-check the live engine version after any update before trusting the channel.
I did not need a freshly created chat. Existing chats worked after the restart.
If your machine is IT-managed or has application-integrity monitoring, think before doing this.
Credit where it is due: this recipe was worked out independently on a second Windows machine and then reproduced here. It is a workaround, not a fix. An official Windows build with a working engine, or a send that fails loudly when delivery is impossible, is still what this issue needs.
The check I called "the only reliable check" is incomplete, and the caveat I added right after it is misdiagnosed. I found this out on Aug 15, the day after posting — flagging it now because I've since seen the same shape of check repeated in this thread, so it isn't only my own comment that's affected.
A cross-session message lands in one of two forms, depending on whether the receiving session is at a turn boundary when it arrives:
receiver idle → type == "user", with origin.kind == "peer" at the top level of the record
receiver mid-turn → first a queue-operation with operation: "enqueue", then consumed as an attachment record (attachment.type == "queued_command") — and there the peer marker sits at attachment.origin.kind, not at the top level
No type == "user" record is ever written for the second case. So "wait for the turn to finish" (my caveat above) does not help: waiting does not make that record appear. The attachment record is what appears instead.
How much this matters is a function of how busy the receiver is, not a property of the check. Two sessions on the same machine, measured Aug 28:
one that happened to be at a turn boundary on every arrival: 0 of 2 missed
one that was almost continuously inside a turn: 22 of 36 missed
Please don't read that second number as a constant — it moves with the receiver's load.
Suggested replacement:
Strongest evidence is the receiver quoting the content back. That spans the whole path and needs no transcript reading at all.
Otherwise accept either consumed form — type == "user"orattachment with attachment.type == "queued_command".
queue-operation / enqueue on its own means delivered but not yet consumed, which is not the same as undelivered.
Only when none of those is present should it be read as not delivered.
One part of the original check I would keep: the no toolUseResult filter. A tool result whose own output happens to contain the message text will otherwise be counted as an inbound message — I broke this myself while measuring the numbers above, and it inflates the count silently.
None of this changes the bug report itself; it only affects how delivery is verified.
Activity
Heeyoung-Ahn commentedon Aug 13, 2026
Likely related / same regression family: #86012 (same environment — Windows 11 10.0.26200, desktop app, bundled CLI 2.1.227; recipient session hangs with hadFirstResponse=false after send_message injection). #86012's mechanism explains what we observed as "silent non-delivery": the recipient query stalls entirely, so from the sender's side the message appears sent-but-never-answered. Our report adds the false-success axis: the sender receives "Message sent" even when nothing is ever processed.
aa09358743 commentedon Aug 13, 2026
Reproduced independently on a second Windows 11 Pro machine (build 10.0.26200, desktop app, native, multiple local sessions, same OS user).
One additional data point that narrows it down: in our repro the message does render in the target session's transcript UI (a "Message from {sender}" card is visible to the human), but it never enters the model's context — when asked, the target session enumerates its received turns and the message is not among them, and no new turn is spawned. So delivery to the UI succeeds; injection into the model's conversation / turn-spawning is what fails. Possibly related to #61718 (queued messages delivered but not actioned).
x123456789172-sys commentedon Aug 13, 2026
Second independent report — different machine, different account, Windows 10.
We hit this on 2026-08-12/13 and spent about five hours misdiagnosing it before
finding this issue. Adding only what isn't already covered above.
Environment (differs from OP)
1.28929.0.0(MSIX/Appx), model Opus 5same symptom, so it is not per-machine or per-account state
Objective check that doesn't rely on asking the model
A successfully injected message appears in the recipient's transcript JSONL as a
userturn wrapping a<cross-session-message …>block. When this bug occurs,that turn is simply absent — the transcript is byte-identical to one where no
message was ever sent.
This is a cleaner signal than asking the receiving session, because it separates
"the model didn't mention it" from "the model never had it":
Restarting does not help
Worth stating explicitly, since a false-success bug often reads like stale
process state. It isn't.
Failure is bidirectional
Not just "session A can't reach session B". Verified in both directions between
the same pair within minutes of each other — each side's message card rendered in
the other's window, and neither model received anything.
queuedvssentis a red herringWhen the target is mid-turn the tool returns:
We initially believed this was the whole story (one of our sessions was genuinely
stuck in a non-terminating turn —
EscandCtrl+Cboth ineffective). It is not:messages returning plain
sentto demonstrably idle sessions were lost identically.Anyone else debugging this will probably go down that path too.
On the "return an error instead of success" request
Strongly seconding this, and it matters beyond this particular regression.
Three layers report independently, and only the last one is load-bearing:
Message sent to session …Because the two observable layers both say "fine", the human operator watching the
recipient's window sees the message arrive and reasonably concludes it was delivered.
That is what cost us the five hours — every intermediate hypothesis we formed
(dead session id, recipient stuck mid-turn, transient app state) was fully consistent
with everything we could observe.
Even after this regression is fixed, a success string that cannot distinguish
"handed to the UI" from "injected into the recipient's context" will make the next
delivery failure equally silent.
Workaround (same as OP, confirming it works)
Coordination moved to the shared git repo: write the message as a file, commit, push;
the recipient runs
git pulland reads it. Slower, but delivery is verifiable —the file either exists at a known commit or it does not.
miocreativ commentedon Aug 13, 2026
Confirming this regression on Windows Desktop, with logs.
Versions
Symptom
send_messagereturns success to the sender ("Message queued for session local_…",and in a later attempt "Message sent to session local_…"), but the target session
never receives anything. No error, no retry, nothing in the target's queue.
The message IS rendered in the target session's UI window, so the user sees it
while the model never does — the assistant truthfully answers "no task arrived"
and looks like it is ignoring instructions. That mismatch is what makes this
expensive: we lost ~2 hours across three sessions before suspecting the tool.
Evidence
Last successful delivery: 2026-08-12 13:57 local (dozens of deliveries worked fine
that day and the day before, including one after a 10-hour idle gap).
After the update: zero deliveries across three freshly created sessions.
Each target's transcript queue contains only messages typed by the human:
grep '"operation":"enqueue"' .jsonl
→ only human-typed entries, no <cross-session-message …> entries
Controlled test on a completely fresh system:
14:34:26 Windows reboot (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
14:36:48 [updater] app start
14:37:03 new session created (target)
14:37:45 main.log: "Sending message to session local_"
→ target queue still contains only its own opening message. Not delivered.
So the app-side dispatch is logged, but the message never reaches the CLI session.
Related warning seen earlier in main.log for another target session:
[warn] [LocalSessionManager] reaped 1 stale pendingEchoUuids for local_
— CLI dropped the isReplay echo
Ruled out
was demonstrably awake, actively chatting with the user, also received nothing)
Impact
Any multi-session workflow that hands work between a planning session and an
execution session is silently broken, and the failure mode actively misleads the
user: the sender is told it worked, the human sees the message in the UI, only the
receiving model never gets it.
Suggested minimum fix
Make the success return value depend on the message actually being enqueued in the
target session, and surface a hard error otherwise — same direction as the 2.1.224
fix, which this regression appears to undo.
DonPuls commentedon Aug 13, 2026
Confirming this on Windows with additional data points, including one that may help narrow the cause: send_message returns success even when the target session ID doesn't exist.
Environment
Claude Desktop 1.28929.0 (d1a6bc), 2026-08-11 build, Windows Server VM
32 GB RAM / 8 logical processors (AMD EPYC 9274F host) — failures occur at ~6% CPU / 14% RAM, so not resource-related
2–3 concurrent Code-tab sessions, same cwd, communicating via ccd_session_mgmt (a Reviewer/Doer code-review workflow that ran ~80 work units cleanly before this regression)
Opus 5, high effort
Loss rate: In one session, the sending agent tracked its own sends: 3 of 4 lost — send returned Message sent (or queued), recipient's transcript (checked directly by the recipient session enumerating its own history) contained nothing. The two load-bearing sends (a work plan and a plan-request) were among the lost.
No addressee validation: A send to a non-existent session ID also returned sent. One of our sessions had inferred its own ID from its transcript-file UUID (which differs from the session-management ID — list_sessions excludes self, so self-ID is guessable-wrong); peers sending to that wrong ID got success results. So sent appears to mean "the call returned," not "a recipient exists," which may be the same underlying cause as silent drops to valid-but-idle sessions.
Correlated hangs: Sessions composing large multi-paragraph send_message payloads (plans, reviews) wedge mid-turn — spinner active, no output, input queues, 8–28 minutes until "Claude Code stopped responding." Two Doer sessions wedged simultaneously, both mid-send. Short sends mostly survive. This matches #86012's recipient-side description; we see it on the sender side too.
Timeline fits the regression window: this workflow was reliable until roughly the 1.28929.0 / runtime 2.1.22x update period. Happy to provide AppData logs from ~12 incidents across Aug 12–13 if useful.
yangzhansong00-google commentedon Aug 13, 2026
Confirming: 2.1.228 does NOT fix this. Clean A/B test below.
Another Windows/MSIX data point, but the new part is the version: I upgraded the CLI to the official 2.1.228 and the failure reproduces exactly. Reverting to 2.1.222 restores delivery within the same minute.
Clean A/B test — only variable changed is the CLI binary
Same target session, same session state (restored after app restart, no activity since), same machine, same sender, same message shape. Nothing else touched between the two runs.
send_messagereturnsentsent.jsonlafter send33479 -> 33479bytes — zero growthelapsed 0.0s, ts2026-08-14T07:41:21.051)I also tested two other targets under 2.1.228 — one with a 4.5 MB context and one freshly created after the upgrade and active seconds before the send. All three: zero bytes of growth. So it is not "the receiver is just slow" and not "only restored sessions are affected" (the freshly created one failed too).
Important diagnostic difference: the 2.1.227 log marker does not appear on 2.1.228
On 2.1.227 the failure is accompanied by:
On 2.1.228 this string never appears. In my
main.logit stayed at 22 occurrences, last one2026-08-13 21:32:49, i.e. zero new occurrences under 2.1.228 while delivery was fully broken.Instead, under 2.1.228 there is no delivery activity logged at all. Under 2.1.222 every send produces:
Under 2.1.228, nothing — not even an attempt.
Binaries (so this is reproducible / verifiable)
Both hashes verified before and after each swap. After the upgrade, the
versionfield inside my own session.jsonlread2.1.228— so the process really was running the new binary, not a stale one.Environment
[CCD-autoupdate] Disabled: MSIX installon every launch — the CLI version is pinned by the desktop app, it does not self-updateThe only landing criterion I found trustworthy
Four signals all report success while nothing is delivered:
send_messagereturn value (sentandqueuedboth lie)list_events— the message is visible in the UI storedrained N deferred send(s)inmain.logflushed held steerscountsThe only reliable check is the receiver's transcript on disk:
userrecord that triggered a turn is only written after that turn ends. So the receiver can never observe its own inbound record within the turn that is processing it — ask a third party to read the receiver's transcript instead, or wait for the turn to finish. I lost time to this before realising the channel was fine at that moment.Suggested triage note
Given 222 works and both 227 and 228 fail — and the issue body states the feature was introduced in 2.1.224+ — it would help a lot to know whether 2.1.224 / 225 / 226 are affected, and whether the fix shipped in 2.1.228 (described as applying to "the first session after install or upgrade") was ever intended to cover the case here, which is all sessions, indefinitely, not just the first one after upgrade.
Happy to run further tests on this machine — I have both binaries staged and can swap them in a couple of minutes.
Heeyoung-Ahn commentedon Aug 14, 2026
Update after upgrading to desktop 1.30096.0.0 / runtime 2.1.229: the false-success axis reported here appears fixed — sends now deliver and render in the recipient session (2.1.228 changelog fix confirmed working). However the recipient session then hangs with zero output (#86012), reproduced on 2.1.229. Closing the loop: our original 'silent non-delivery' was likely #86012's hang observed from the sender side. This issue's remaining value is the platform question (docs still say native Windows unsupported, yet delivery now works there).
arthurmoraesfernandes-afk commentedon Aug 14, 2026
Same regression window and signature — #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, anddialogExpiry(~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. SettingcrossSessionInbound: "accept"in user settings does not unblock it (tested on running, cold-started, and post-restart fresh sessions).NimaPirmoradian commentedon Aug 14, 2026
Independent confirmation on a second Windows machine, with the boundary measured to the minute. Same platform and install type: Windows 11, native (not WSL), Claude Code desktop app with its bundled/managed runtime.
Same boundary, measured from this machine's own logs
The desktop app's own version (
app-1.26832.0to1.28929.0) moved in the same step. Worth noting for anyone bisecting:1.28929.0was staged at 01:55 on 08-12 but did not become the running instance until 19:27 (main.loglogs the earlier start and then "Not main instance, returning early"), so the app-version timestamp alone is misleading.Last successful cross-session delivery here:
2026-08-12T14:52:43Z, about 2h35m before the runtime changeover. Scanning all 1,068 session transcripts under~/.claude/projects, restricted to top-leveltype:"user"records whose content begins with<cross-session-message, gives 582 delivered peer turns, and none after that timestamp.The failure signature, from the app's own watchdog
This may be the most useful part. When the desktop app reaps a wedged session it writes a post-mortem to
%APPDATA%\Claude\logs\main.log. For every target of a failed peer send:(1001s / 1008s / 1015s / 964s / 771s across the batch, all
hadFirstResponse=false.)seconds_since_stderr=neverplus zerostderr taillines means the spawned managed runtime lived about 16 minutes and emitted zero bytes on stdout AND stderr. It never even sentsystem/init. The same binary, reached through the same code path by an ordinary user-typed message to the same session, initialises in about 4 seconds and answers normally.So the wedge follows the record, not the spawn: a record carrying
origin.kind:"peer"never becomes a turn, warm or cold, new chat or old.On the sending side,
Sending message to session ...is logged andMessage sentis returned, but the enqueue is fire-and-forget. Nothing in the path awaits an init or a turn echo, so success is returned unconditionally. That is why nothing surfaces to the user until the roughly 16-minute reaper fires.Ruled out by test, not by inference
"crossSessionInbound": "accept"in user settings plus a full app restart: no change, on both an existing chat and a brand-new chat created after the setting.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,DISABLE_TELEMETRY,DO_NOT_TRACK,DISABLE_GROWTHBOOK): not set at User, Machine or Process scope. A full sweep of both persistent scopes finds no related variable.Related, same signature: #86069 and #86298.
One data point for #86298 specifically, since it proposes a hold-for-approval that expires: setting
crossSessionInbound: "accept"here, which by the documented precedence should remove any default hold, changed nothing on either an existing or a freshly created chat. And on this machine the death clock is about 1000s, not about 5 min, and it comes from the app's inactivity reaper rather than a dialog expiry. So if the hold hypothesis is right,acceptis not being honoured on this path.Why this one hurts: three weeks of daily use, then silence
We were not experimenting with this. We had been using it in production for three weeks. Two Claude Code sessions coordinated an autonomous test loop on one machine, handing work back and forth, and cross-session messaging carried it: 582 delivered messages before it stopped. Whether the capability was ever formally supported on Windows or not, it was discoverable, it reported success, and it worked reliably every day, so we built on it.
That is what makes the silent failure costly. Nothing announced a change. The API kept returning
Message sent. Both sides kept behaving as if delivery had happened, and the only signal was work quietly not getting done. We spent a day diagnosing our own configuration before finding this issue.Request
Please could this be prioritised: restore delivery on Windows if the platform is meant to be supported, or, if it genuinely is not, make the send fail loudly instead of returning success. Either is workable. The present state, where the call reports success and nothing is delivered, is the one that cannot be designed around.
A one-line release note would also have saved a lot of time here.
Happy to supply the full
main.logextracts, transcript scans or the reaper post-mortems if any of it is useful.(For anyone blocked meanwhile: we fell back to passing the payload through a file on disk and using the message only as a doorbell, the same conclusion as the reporter.)
NimaPirmoradian commentedon Aug 16, 2026
Addendum to my comment above. The public changelog answers the "was this ever supported" question that I left open, and it lines up exactly with the boundary measured on my machine.
2.1.224 (Aug 7) is where the feature was introduced, and Windows was never in scope
Set against the runtime versions on this machine:
So the likely shape is not "a working Windows feature regressed". It is that before 2.1.224 there was no runtime-level cross-session messaging at all, and the desktop app's own delivery path worked on Windows by default. From 2.1.224 the runtime owns peer messaging, scoped to macOS and Linux. My machine crossed that boundary in a single jump and the capability changed owner underneath it.
That reading fits every negative result I reported above: no setting helps, a freshly created chat does not help, target liveness does not help, and the spawned runtime takes the record and emits nothing on either pipe.
Two more changelog entries that bear directly on this
2.1.224, same release:
The identical false-success problem was recognised and fixed for the teammate path in the very release that introduced cross-session sending. The cross-session path on Windows still returns success on a delivery that never happens.
2.1.225:
"Staying parked without a notice or expiry" is the exact symptom here, fixed for two other contexts.
A note for anyone advising a settings change
2.1.232 added the
/configrows for "Dialog expiry" and "Messages from your other sessions" (cross-session inbound accept/hold/refuse). The desktop app pins its own bundled runtime: mine is 2.1.229, while the CLI changelog is already at 2.1.233. So those/configrows do not exist for desktop users on this runtime, and neither does any UI for the inbound setting. WritingcrossSessionInboundintosettings.jsonby hand is possible, and as reported above it changed nothing here.This also means desktop users cannot reach a CLI fix by updating anything themselves. The bundled runtime moves only when the desktop app bumps it.
The request, restated more precisely
Given the above, this is less "please fix a regression" and more:
Message senton a platform where delivery is out of scope is the part that cost us a day and three weeks of a working workflow.Happy to provide the runtime version timeline, the app watchdog post-mortems, or the transcript scans from my earlier comment if any of it helps.
NimaPirmoradian commentedon Aug 16, 2026
Addendum to my comment above. The public changelog answers the "was this ever supported" question that I left open, and it lines up exactly with the boundary measured on my machine.
2.1.224 (Aug 7) is where the feature was introduced
Set against the runtime versions on this machine:
One reading is that before 2.1.224 there was no runtime-level cross-session messaging at all, the desktop app's own delivery path worked on Windows, and from 2.1.224 the runtime owns peer messaging with macOS and Linux in scope. My machine crossed that boundary in a single jump.
I want to flag the counter-evidence rather than push that reading too hard, because #86629 carries the
regressionlabel from triage, and its reporter measured delivery working on 2.1.219, 2.1.221 and 2.1.222 with 74 calls cross-checked. If this were purely a documented platform scope being enforced,regressionwould be the wrong label. So the honest position is: the boundary is certain, the intent behind it is not.Two more changelog entries that bear directly on this
2.1.224, same release:
The identical false-success problem was recognised and fixed for the teammate path in the very release that introduced cross-session sending. The cross-session path on Windows still returns success on a delivery that never happens.
2.1.225:
"Staying parked without a notice or expiry" is the exact symptom here, fixed for two other contexts.
A note for anyone advising a settings change
2.1.232 added the
/configrows for "Dialog expiry" and "Messages from your other sessions" (cross-session inbound accept/hold/refuse). The desktop app pins its own bundled runtime: mine is 2.1.229, while the CLI changelog is already at 2.1.233. So those/configrows do not exist for desktop users on this runtime. WritingcrossSessionInboundintosettings.jsonby hand is possible, and as reported above it changed nothing here.And a desktop-app update does not necessarily move the runtime. Mine updated to 1.30096.5 on 2026-08-15 and delivery still fails, because
%APPDATA%\Claude\claude-code\still contains only 2.1.229. Anyone re-testing after an app update should check that folder first, or they are re-testing the same runtime.The request, restated more precisely
Message sentwhen delivery cannot happen is the part that cost us a day and three weeks of a working workflow.Happy to provide the runtime version timeline, the app watchdog post-mortems, or the transcript scans from my earlier comment if any of it helps.
aa09358743 commentedon Aug 17, 2026
Cross-session
send_messageis silently lost: message is persisted and rendered, but never enters the recipient's model contextRelated to #86275. On runtime 2.1.232 the symptom described there ("reports success but never arrives") still reproduces, but the actual mechanism is narrower and more dangerous than the original report suggests — and it defeats the obvious ways of verifying delivery.
Environment
claude-opus-5)Two local sessions in the same repo:
local_38bdcd6f-…, branchclaude/send-message-cross-session-test-095e08, running in a worktree.C:\projects\tim-company. Believed to have been created by A viaspawn_task, i.e. a background-task session. (See "Unrun control" below — this may be load-bearing.)What happened
B → A works. B sent
B#1. Tool returned:A received it and quoted it back verbatim. Delivery confirmed.
A → B silently fails. A sent
A#1, waited, then sentA#2, and waited ~10 minutes. Both calls returned success on A's side.On B's side, three layers came apart:
B was never woken, and B's model never saw either message.
The worst part. The human then typed into B's window manually. B woke and responded normally — the session was alive. But the two pending messages were still not included in that turn's context. This is not delayed delivery and not a failure to wake: the content is permanently lost to the recipient model.
B's
isRunningstayedtruewhilelastActivityAtwas frozen at the moment B's task terminated.Why this is worse than "message not delivered"
Every available way of verifying delivery returns a false positive.
list_eventson B, saw two[user] <cross-session-message from=…>entries, and concluded delivery had succeeded. It then rewrote its diagnosis away from Windows desktop app: cross-session send_message silently fails (reports success, never delivered) after runtime 2.1.222→2.1.227 auto-update #86275 and toward "deadlock." That conclusion was wrong, and it was reached from real evidence.The only party that knows the message never arrived is the recipient model — and it cannot know, because from its perspective nothing happened. There is no observable signal that content is missing.
For multi-agent / multi-session workflows this means handoffs can fail with no error anywhere in the system, and any post-hoc audit of the transcripts will show the handoff as successful.
Two return strings map to recipient state
Worth noting, since #86275 quotes only the first form:
Message queued for session … it will be processed after the in-flight turn finishes if that session stays healthy.Message sent to session ….Both are reported as success. In our repro the queued form was returned for a target whose turn had already reached a terminal
[result] done (success)state — so it was queued behind a turn that had already finished and would never finish again.Expected vs actual
Expected: either the message enters the recipient's context and triggers a turn, or the send call fails with an error naming the reason.
Actual: the call reports success, the message is persisted and rendered, and the recipient model never sees it — including on subsequent turns triggered by other means.
Steps to reproduce
spawn_task.[result] done (success).mcp__ccd_session_mgmt__send_messagetargeting B. Observe the success return.Unrun control (please don't over-read our result)
We have not yet run the same test against a manually opened session as the recipient. Our B was (as far as we can tell) a
spawn_task-created background session that had reached a terminal state. So this repro may be scoped to "messages to a completed background-task session" rather than the general case in #86275. Both are real bugs; conflating them would be misleading.Suggested fixes, in priority order
isRunningfor terminated task sessions — it staystruewith a frozenlastActivityAt, solist_sessionsmisreports the state that callers would use to decide whether sending is safe.NimaPirmoradian commentedon Aug 18, 2026
Working fix on Windows: swap the desktop app's bundled engine to >= 2.1.232
Cross-session messaging is working again on my Windows machine, verified both directions on disk. Posting the full recipe since nobody in this thread or #86629 / #86069 has had a workaround.
The key point: there are TWO locks and you need both
Neither alone works. Engines older than 2.1.232 ignore the setting entirely. That is why I reported earlier in this thread that
crossSessionInbound: "accept"changed nothing: I was on 2.1.229, so the setting was never read. That negative result of mine was untestable, not a refutation, and I want to correct it explicitly because it may have sent others down the wrong path.Confirmed on my machine: engine 2.1.234 alone was not tried in isolation here, but on the machine where this recipe was developed, 2.1.234 alone also failed and only the pair worked.
Recipe
1. Check what you have.
If the live engine is already >= 2.1.232, skip to step 3.
2. Get a newer engine and swap it in. The npm package ships the same native binary.
Notes: Windows lets you rename a running exe, so open chats keep working on the old process. Leave
.verifiedalone even though it is now stale; the app accepted it with no error and no re-download. The folder keeps its old name and the process reports the new version.3. Add the setting to
%USERPROFILE%\.claude\settings.json, preserving your existing keys:{ "crossSessionInbound": "accept" }4. Quit the app completely, including the tray icon, and reopen.
Verify, and do not trust the usual signals
The sender's
Message sent, the card that renders in the target window, andmain.logall report success on a delivery that never happened. The only honest check is the receiving session's own transcript:Success looks like this, in the recipient's jsonl:
On my machine: enqueue at +2s, model reply at +24s, and the return direction landed the same way.
Caveats, stated plainly
.bakfiles back, restart.Credit where it is due: this recipe was worked out independently on a second Windows machine and then reproduced here. It is a workaround, not a fix. An official Windows build with a working engine, or a send that fails loudly when delivery is impossible, is still what this issue needs.
codebytere-ant commentedon Aug 25, 2026
duplicate of #86012
yangzhansong00-google commentedon Aug 28, 2026
Correction to my comment above (Aug 14).
The check I called "the only reliable check" is incomplete, and the caveat I added right after it is misdiagnosed. I found this out on Aug 15, the day after posting — flagging it now because I've since seen the same shape of check repeated in this thread, so it isn't only my own comment that's affected.
A cross-session message lands in one of two forms, depending on whether the receiving session is at a turn boundary when it arrives:
type == "user", withorigin.kind == "peer"at the top level of the recordqueue-operationwithoperation: "enqueue", then consumed as anattachmentrecord (attachment.type == "queued_command") — and there the peer marker sits atattachment.origin.kind, not at the top levelNo
type == "user"record is ever written for the second case. So "wait for the turn to finish" (my caveat above) does not help: waiting does not make that record appear. Theattachmentrecord is what appears instead.How much this matters is a function of how busy the receiver is, not a property of the check. Two sessions on the same machine, measured Aug 28:
Please don't read that second number as a constant — it moves with the receiver's load.
Suggested replacement:
type == "user"orattachmentwithattachment.type == "queued_command".queue-operation/enqueueon its own means delivered but not yet consumed, which is not the same as undelivered.One part of the original check I would keep: the
no toolUseResultfilter. A tool result whose own output happens to contain the message text will otherwise be counted as an inbound message — I broke this myself while measuring the numbers above, and it inflates the count silently.None of this changes the bug report itself; it only affects how delivery is verified.