[BUG] `agents_cross_session_inbox` gate off: no UDS inbox is bound, `@` lists no peers, but send_message still reports success
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
On Windows, the CLI never binds its cross-session messaging inbox, because the remote feature gate is off:
[uds-messaging] Skipped: cross-session messaging gate off (will late-bind if a GrowthBook refresh enables it)
Nothing surfaces this anywhere. The @-mention picker (advertised in the 2.1.232 release notes) just lists no sessions, SendMessage / ccd_session_mgmt.send_message stay exposed as usable tools, and the desktop send_message returns success. The feature fails open — it looks available and reports success — instead of failing closed with a stated reason.
Filed separately from the desktop held-steer regression (#86385, #86386, #86014, #86279): those describe a message that reaches the target session's queue but never triggers a responding turn. This report is about the layer below — on this machine the inbox listener is never bound at all, so CLI-to-CLI peer discovery yields nothing.
Evidence
1. The gate is off (CLI debug log)
From ~/.claude/debug/<uuid>.txt, on CLI 2.1.232:
[DEBUG] [uds-messaging] Skipped: cross-session messaging gate off (will late-bind if a GrowthBook refresh enables it)
Reproduced on two independent runs. It is the only [uds-messaging] line emitted.
2. The gate identifier
In the 2.1.232 bundle, the branch that emits the line above sits in the same if/else chain as the bind path: when the gate is on, that branch calls startCrossSessionInbox() and reports the outcome under the identifier agents_cross_session_inbox (bind_failed, bind_failed_late, plus a degraded-cause variant). The late-bind path logs [uds-messaging] Late bind: gate enabled by a GrowthBook refresh after startup.
Other strings in the same area: CLAUDE_CODE_MESSAGING_SOCKET, CLAUDE_CODE_MESSAGING_TOKEN, setup_uds_messaging_ms, [uds-messaging] Skipped: remote thin client.
3. Negative control: two clean CLI 2.1.232 sessions cannot see each other
Two interactive sessions, same machine, same working directory, both live, both persisting transcripts (~22 KB each). Typing @ in session A lists only files and agents — no session entries. No error, and no hint that peer discovery is unavailable.
Rigor note: an earlier attempt was invalid because the CLI inheritedCLAUDE_CODE_CHILD_SESSION=1from its parent process, which disables transcript persistence (the TUI does warn about that one:Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker). The run above was relaunched with those markers cleared, and that warning was gone.
4. No messaging socket in a desktop session's environment
A Claude Desktop-hosted session's environment contains CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_HOST_SESSION_ID, CLAUDE_CODE_ENTRYPOINT=claude-desktop and friends, but neither CLAUDE_CODE_MESSAGING_SOCKET nor CLAUDE_CODE_MESSAGING_TOKEN.
Actual behaviour
The gate is off and nothing says so. @ returns an empty peer list that is indistinguishable from "no other sessions running", and send_message returns success.
Why this matters
The 2.1.232 release notes advertise @-mention by session name, SendMessage delivery to a bare name, unique per-machine session names, and a /config row for cross-session inbound accept/hold/refuse. With the gate off, all of it is inert with no diagnostic. Users — and agents driving these tools — cannot distinguish "feature disabled for my account" from "bug" or from "no peers running".
Asks
- Confirm whether
agents_cross_session_inboxis intended to be off for general accounts at this version, and what the rollout state is. - Make the gate state observable (
/statusor/doctor). - Fail closed: when the inbox is not bound,
SendMessageandccd_session_mgmt.send_messageshould refuse with the reason rather than report success. - Make the
@picker distinguish "peer messaging unavailable" from "no peers found".
What Should Happen?
Either the gate is on and peer sessions are discoverable, or the gate is off and that fact is visible:
- The
@picker states that peer messaging is unavailable, instead of returning an empty list that is indistinguishable from "no other sessions are running". SendMessage/ccd_session_mgmt.send_messagefail closed — they refuse with the reason (inbox not bound / feature gated off) instead of returning success./statusor/doctorreports whether the cross-session inbox is bound.
At minimum, a gated-off feature should not report successful delivery.
Error Messages/Logs
The only [uds-messaging] line emitted at startup, from ~/.claude/debug/<uuid>.txt (CLI 2.1.232, Windows):
[DEBUG] [uds-messaging] Skipped: cross-session messaging gate off (will late-bind if a GrowthBook refresh enables it)
No error is shown in the TUI, and no corresponding line appears in the Claude Desktop main log.
Steps to Reproduce
On Windows:
- Install CLI 2.1.232:
npm i -g @anthropic-ai/claude-code - Open two interactive
claudesessions in the same directory. Launch them from a shell with noCLAUDE_CODE_*session markers set — in particularCLAUDE_CODE_CHILD_SESSION,CLAUDE_CODE_SESSION_ID,CLAUDE_CODE_HOST_SESSION_ID,CLAUDE_CODE_ENTRYPOINT,CLAUDECODE. (If the TUI showsTranscript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker, the shell is contaminated and the test is invalid.) - Send one prompt in each session so both sessions exist. Confirm both are persisting transcripts.
- In session A, type
@.
Observed: the picker lists only files and agents. No peer sessions, no error, no indication that peer messaging is unavailable.
- To see the cause, run
claude --debugand inspect~/.claude/debug/<uuid>.txt:
[DEBUG] [uds-messaging] Skipped: cross-session messaging gate off (will late-bind if a GrowthBook refresh enables it)
Separately, from Claude Desktop, calling ccd_session_mgmt.send_message at another local session returns success to the caller.
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.232 (Claude Code) — standalone CLI; Claude Desktop 1.30096.1.0 bundles 2.1.229
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Windows Terminal
Additional Information
Precise environment:
- Windows 11 Pro 10.0.22621
- Claude Desktop 1.30096.1.0 (Microsoft Store / MSIX; auto-update disabled by design on MSIX installs), bundling CC runtime 2.1.229
- Standalone CLI installed via npm: 2.1.232
Both the desktop-hosted sessions and the standalone CLI sessions on this machine are affected — no [uds-messaging] bind ever happens.
Related but distinct, for triage: #86385, #86386, #86014, #86279 describe the desktop-side symptom (message reaches the target session but no responding turn is triggered; a held steer is only flushed once the user types into the target session). This report is only about the gate/inbox layer and the absence of any signal that the feature is disabled.
3 Comments
Thanks for the report. Following up on the "sender says success, recipient refused" mismatch: a fix is up for review that (1) makes a refused cross-session message report back to the sender as refused (with the reason) instead of appearing sent, and (2) makes
ListAgentsand Remote Control mark a session that cannot currently receive cross-session messages, and refuse the send up front rather than posting into the void. It will note here once it lands and which release carries it. Separately, the Windows-side rollout that caused most of these "refused" results is being enabled independently of the code fix.A datapoint from the other side of this gate, plus a warning about an adjacent workaround.
The gate is on for at least one macOS account. CLI 2.1.234 on macOS (Darwin 25.5.0), running under the desktop app (manually swapped into the app's runtime directory — context in #86298): the UDS inbox binds normally here.
/tmp/cc-socks/holds a socket per live session (nine as I write this), peer discovery lists live sessions (seven just now), and the channel has carried real traffic in both directions as recently as yesterday — outbound sends acknowledged with typed delivery receipts, and inbound peer replies arriving taggedfrom="uds:/tmp/cc-socks/<pid>.sock". I can't quote the[uds-messaging]line to match yours because I don't have debug logging enabled; the bound sockets, populated peer list, and completed round-trips are the same signal from the outside.So this isn't a feature that's globally off — it's enabled somewhere and silently absent elsewhere, which I think strengthens your fail-closed argument: a gate that's live for some accounts and absent for others is exactly the case where "no peers listed" needs to state a reason, and where
send_messagereturning success is most misleading.Warning for anyone arriving from the neighbouring threads: the two-lock workaround circulating for the desktop regression (engine >= 2.1.232 plus
"crossSessionInbound": "accept") will not help with this issue. I verified that recipe on macOS yesterday and it does fix the desktop path — but that path isccd_session_mgmt send_message, where the message reaches the target session and fails to become a turn. This report is about the UDS listener never binding at all. Different channel, different mechanism; the setting doesn't reach it. Worth stating explicitly since the two failure modes read almost identically from the sender's side, and both end in a success receipt.The rollout has reached this machine; re-verified both fixes from your comment above on Windows.
Environment
2.1.246(2.1.237also present)Re-ran the repro from the original issue: two interactive sessions on the same machine, peer listing in one, then a send.
Peer listing — fixed.
ListAgentsreturns a roster, and a newly started CLI shows up within ~12 s with a status:On 2026-08-14 the
@picker on this machine returned only files and agents, with nothing indicating that peer messaging was unavailable. (Today's probe is theListAgentstool; I have not separately re-checked the picker UI.)Residual: when that session's process was killed it was removed from the roster rather than listed and marked as unable to receive, so a caller cannot distinguish "no such session" from "was there, cannot receive now".
Refusal reporting — fixed. Sending to the killed session:
No false
success, no silent queue.Residual: the message conflates an unknown name with a known-but-unreachable one.
I could not exercise the specific "inbox not bound" refusal — every live session now binds one, and
2.1.229, the last runtime here that did not, has since been cleaned off disk.Observability (the
/status//doctorask from the original report) — partially there. The bind is now loudly logged:[uds-messaging] Listening: \\.\pipe\LOCAL\cc-msg-<hash>appears in~/.claude/debug/<uuid>.txt(observed 2026-08-25, post-rollout) whereSkipped: cross-session messaging gate offused to be the only line. So bind state is readable from the debug log; I have not re-checked whether/statusor/doctorsurface it.Delivery — checked by observable side effect, not by the return value
Two independent probes, each asking the target to echo a marker string, with nobody touching the target window:
Both showed the same three things: the target's transcript gained a
<cross-session-message from="...">user turn, the target started a turn on its own and replied with the marker, and the desktop log had noflushed held steersline that day — so neither round trip came from the "type something into the target to flush it" workaround.Rollout vs. code fix — these are separable here
Each session writes
~/.claude/sessions/<pid>.json, and a session that bound an inbox records amessagingSocketPaththere — so counting those files reads the rollout state passively, with no sends involved. Measured on 2026-08-21:| runtime | sessions | with inbox |
|---|---|---|
| 2.1.229 | 17 | 0 |
| 2.1.234 | 36 | 6 |
The 6 bound sessions are exactly the 2.1.234 sessions started after a flag flip that landed between 2026-08-20 06:50 and 2026-08-21 10:37 JST (bracketed by the last non-binding and the first binding session start). Neither condition alone was enough: 2.1.234 sessions started before the flip did not bind, and 2.1.229 sessions started after it did not either. Everything above was therefore measured with both in place — new runtime, flag on.
One last observation: a successful delivery no longer emits the
Sending message to session <id>line the desktop log used to write for every attempt. Delivery appears to have moved onto the named-pipe inbox rather than the previous queue, so I can't say whether the old queue path — the one behind theflushed held steersbehaviour — is still reachable in other configurations; only that my sends no longer take it.Thanks for the follow-up.