Skip to content

Windows desktop app: cross-session send_message silently fails (reports success, never delivered) after runtime 2.1.222→2.1.227 auto-update #86275

Closed as duplicate

Description

@Heeyoung-Ahn

Summary

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)

  1. Session A sends a message to session B via the desktop app's session messaging.
  2. The tool returns success: Message sent to session local_… ("<title>").
  3. Nothing ever appears in session B — confirmed by a human watching session B (not just by the sender).
  4. 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

  1. Windows 11, desktop app with runtime ≥ 2.1.224 (observed on 2.1.227).
  2. Open two local sessions.
  3. From session A, ask Claude to send a message to session B (session-management send_message).
  4. Observe: sender gets "Message sent…", session B never receives it.

Happy to provide timestamps or additional logs if useful.

Activity

  1. Heeyoung-Ahn commented on Aug 13, 2026

    @Heeyoung-Ahn
    Author

    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.

  2. aa09358743 commented on Aug 13, 2026

    @aa09358743

    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).

  3. x123456789172-sys commented on Aug 13, 2026

    @x123456789172-sys

    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.

  4. miocreativ commented on Aug 13, 2026

    @miocreativ

    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.

  5. DonPuls commented on Aug 13, 2026

    @DonPuls

    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.

  6. yangzhansong00-google commented on Aug 13, 2026

    @yangzhansong00-google

    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.

    CLI 2.1.228 CLI 2.1.222
    Target session same session same session
    Target state restored after restart, not activated since identical
    send_message return sent sent
    Receiver .jsonl after send 150 s watch: 33479 -> 33479 bytes — zero growth landed immediately (elapsed 0.0s, ts 2026-08-14T07:41:21.051)
    Receiver actually replied no yes

    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.

    Binaries (so this is reproducible / verifiable)

    2.1.228  FileVersion 2.1.228.0   296,308,896 bytes
             sha256 841EFCE70B048AE03C752F79000CB6BE5C2C2C4D0478F3EAAA762494ADE9DAF5
    2.1.222  FileVersion 2.1.222.0   279,014,048 bytes
             sha256 032CB799D2ABFAA6CA440F6458304B9A2A250521063D21EBCEA7F3C77C443DB7
    

    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:

    1. send_message return value (sent and queued both lie)
    2. list_events — the message is visible in the UI store
    3. drained N deferred send(s) in main.log
    4. flushed held steers counts

    The only reliable check is the receiver's transcript on disk:

    a record with type == "user", no toolUseResult, 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.

  7. Heeyoung-Ahn commented on Aug 14, 2026

    @Heeyoung-Ahn
    Author

    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).

  8. arthurmoraesfernandes-afk commented on Aug 14, 2026

    @arthurmoraesfernandes-afk

    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).

  9. NimaPirmoradian commented on Aug 14, 2026

    @NimaPirmoradian

    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.

    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, 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.)

  10. NimaPirmoradian commented on Aug 16, 2026

    @NimaPirmoradian

    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:

    1. Please bring cross-session messaging to Windows on the desktop app, or say plainly that it will not come, so people can plan.
    2. 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.
    3. 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.

  11. NimaPirmoradian commented on Aug 16, 2026

    @NimaPirmoradian

    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

    1. Please make cross-session messaging work on Windows on the desktop app, or state plainly that it will not, so people can plan.
    2. 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.
    3. 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.

  12. aa09358743 commented on Aug 17, 2026

    @aa09358743

    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

    1. 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.

    2. 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.

    3. 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.

    4. 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.

    5. 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 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

    1. From session A, create session B via spawn_task.
    2. Let B complete its task and reach [result] done (success).
    3. From A, call mcp__ccd_session_mgmt__send_message targeting B. Observe the success return.
    4. Observe the message appear in B's UI and transcript.
    5. Type into B's window manually to force a turn.
    6. 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

    1. 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.
    2. 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.
    3. 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.
    4. Document that reading the recipient's transcript does not verify delivery. Transcript presence and context presence are different things; right now nothing communicates that.
  13. NimaPirmoradian commented on Aug 18, 2026

    @NimaPirmoradian

    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:

    Select-String -Path "$env:USERPROFILE\.claude\projects\*\*.jsonl" -Pattern "YOUR-UNIQUE-TOKEN" -List

    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.

  14. codebytere-ant commented on Aug 25, 2026

    @codebytere-ant

    duplicate of #86012

  15. yangzhansong00-google commented on Aug 28, 2026

    @yangzhansong00-google

    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:

    • 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:

    1. Strongest evidence is the receiver quoting the content back. That spans the whole path and needs no transcript reading at all.
    2. Otherwise accept either consumed form — type == "user" or attachment with attachment.type == "queued_command".
    3. queue-operation / enqueue on its own means delivered but not yet consumed, which is not the same as undelivered.
    4. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:desktopbugSomething isn't workinghas reproHas detailed reproduction stepsplatform:windowsIssue specifically occurs on Windows

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions