Local peer-messaging inbox socket fails to bind for one of two identical sessions (one-way cross-session messaging)

Status Open
Reported on v2.1.224
Maintainer reply None cached
Activity 3 comments · opened Aug 7, 2026

Version: 2.1.224
Platform: macOS (Darwin 25.6.0), Apple Silicon
Area: cross-session messaging (SendMessage / ListAgents, /tmp/cc-socks peer sockets)

Summary

Two Claude Code CLI sessions launched from terminals on the same Mac, with the same
binary, same flags, and same cwd, 28 seconds apart. One bound a peer-messaging inbox
socket; the other never did. No configuration difference distinguishes them.

The result is a silently one-way channel: session A can message session B, but B cannot
message A. B's sends instead fall through to A's Remote Control bridge registration and
are refused with a message about device-gated bridge sessions — which misattributes the
cause to the bridge and sent us down three rounds of wrong hypotheses.

The two sessions

| | Session A ("Infra Master") | Session B ("Opus Remote") |
|------------------------------------|---------------------------------------|------------------------------------|
| pid / sessionId | 1821 / 58f71b61-20f3-454e-bc48-fe3d665b9e37 | 1967 / 73e341ea-2928-4871-a0a5-6e8b7adca644 |
| launch | claude --dangerously-skip-permissions | claude --dangerously-skip-permissions |
| started (ET; JSON procStart is UTC) | Fri Aug 7 18:00:27 2026 | Fri Aug 7 18:00:55 2026 |
| version / kind / entrypoint | 2.1.224 / interactive / cli | 2.1.224 / interactive / cli |
| cwd | same repo | same repo |
| Remote Control attached | yes (bridgeSessionId present) | yes (bridgeSessionId present) |
| CLAUDE_CODE_MESSAGING_SOCKET | UNSET | /tmp/cc-socks/1967.sock |
| messagingSocketPath in session JSON | absent | present |
| inbox socket on disk | none | bound |

Evidence

~/.claude/sessions/1821.json — note the absent messagingSocketPath:

{"pid":1821,"sessionId":"58f71b61-20f3-454e-bc48-fe3d665b9e37","cwd":"…/Wyckoff Trading App",
 "procStart":"Fri Aug  7 22:00:27 2026","version":"2.1.224","peerProtocol":1,
 "kind":"interactive","entrypoint":"cli","name":"Infra Master","status":"idle",
 "bridgeSessionId":"session_01DLDiVR2gtwwJAFU55UPZDz"}

~/.claude/sessions/1967.json — same shape, plus the socket path:

{"pid":1967,"sessionId":"73e341ea-2928-4871-a0a5-6e8b7adca644","cwd":"…/Wyckoff Trading App",
 "procStart":"Fri Aug  7 22:00:55 2026","version":"2.1.224","peerProtocol":1,
 "kind":"interactive","entrypoint":"cli","messagingSocketPath":"/tmp/cc-socks/1967.sock",
 "name":"Opus Remote","status":"busy",
 "bridgeSessionId":"session_01FfXMJ8xLtrpezzpsF1dGqv"}

Socket directory — only one of the two sessions is represented:

$ ls -la /tmp/cc-socks/
drwx------   3 user  wheel   96 Aug  7 18:00 .
srw-------   1 user  wheel    0 Aug  7 18:00 1967.sock

Both processes alive throughout:

$ ps -p 1821 -o pid,lstart,command
 1821 Fri Aug  7 18:00:27 2026  claude --dangerously-skip-permissions
$ ps -p 1967 -o pid,lstart,command
 1967 Fri Aug  7 18:00:55 2026  claude --dangerously-skip-permissions

Env var, measured in each session:

Session A: echo "${CLAUDE_CODE_MESSAGING_SOCKET:-UNSET}"  →  UNSET
Session B: echo "${CLAUDE_CODE_MESSAGING_SOCKET:-UNSET}"  →  /tmp/cc-socks/1967.sock

Observed behavior

  • A → B: every message delivered, five for five. A dials B's socket successfully.
  • B → A: every attempt failed. ListAgents in B shows A only as a Remote Control row

(never as a local peer), and SendMessage returns:

> 'Infra Master' matches a Remote Control session on this account, and those are
> reply-only from here: messageable only in reply, after it messages you first.

Because A has no inbox socket, its only advertised registration is bridgeSessionId, so
B's send is routed to the device-gated bridge path and refused.

Hypotheses ruled out before the socket was found

  1. Reply-grant staleness — a reply sent in the same tool round as delivery: still refused.
  2. Name divergence — A's from-name initially differed from its ListAgents row; after

/rename aligned them exactly: still refused.

  1. Bridge state — disconnect/reconnect of Remote Control on B: still refused. (With B's

bridge off, ListAgents returned "No reachable agents" — A did not appear as a local
peer even with the bridge out of the way, which is consistent with the missing socket.)

  1. Session class — both are terminal-born local interactive/cli sessions, so the

"bridged session" framing in the error text does not describe the real cause.

Expected vs actual

Expected: every local interactive session binds /tmp/cc-socks/<pid>.sock at startup
and is addressable by name from other local sessions.

Actual: binding silently did not happen for one session. Nothing surfaced the failure —
no warning at startup, no indication in ListAgents, and the eventual SendMessage error
pointed at the bridge instead of the missing inbox.

Suggested fixes

  1. Fail loudly if the inbox socket does not bind — a startup warning naming the path is

enough; the silence is what made this take an hour to localize.

  1. Distinguish the two refusal cases in the SendMessage error. "Peer has no local

inbox socket (messaging unavailable — try restarting that session)" is actionable;
the current bridge/device-gating text is actively misleading when the peer is local.

  1. Show socket state in ListAgents so a one-way channel is visible before someone

relies on it, rather than after a failed round-trip.

  1. Investigate the race — the two sessions started 28s apart and /tmp/cc-socks itself is

timestamped 18:00, so directory creation vs. bind ordering between concurrently
starting sessions is worth checking.

View original on GitHub ↗

3 Comments

sfainbraun-KC3YDL · 23 days ago

Follow-up with a diagnostic data point: a plain restart of the affected session (quit + claude --continue to resume the same conversation, same flags, same cwd) re-bound the socket — CLAUDE_CODE_MESSAGING_SOCKET now set, messagingSocketPath present in the session registration JSON, socket file on disk. Full two-way messaging then verified between the two sessions (reply delivered in ~36 s). Since nothing about the session's configuration changed across the restart, this supports the startup-race hypothesis in suggested-fix #4 rather than anything environmental. Also of note for fix #2: during the failure window, the SendMessage error pointed at Remote Control device-gating, which cost us three falsified hypotheses (reply-grant timing, name divergence via /rename, bridge disconnect/reconnect) before the missing socket was found.

sfainbraun-KC3YDL · 23 days ago

Sharper repro hypothesis, from the operator's account of the session sequence plus registration evidence: the failing session was a resume of a conversation that previously had Remote Control attached, launched while that remote re-attached; the session that bound fine 28s later was a fresh conversation (also with a remote). After tonight's fix-restart we also observed stale bridgeSessionId carryover: the restarted session's registration still carried the OLD bridge id while no remote was connected at all; an explicit disconnect/reconnect minted a new id. So the failing conjunction looks like resume + prior bridge state present at startup (bind skipped or lost while the bridge re-attaches), rather than two concurrently-starting sessions racing over the socket directory — consistent with all four sessions we've now observed: fresh+bridge → bound; resume+bridge-at-startup → NOT bound; resume+no-bridge → bound; attach-after-start → stays bound.

sfainbraun-KC3YDL · 23 days ago

Final corroborating detail from the operator, tying the stale-state observation to visible symptoms: when connecting Remote Control to the restarted session, the client reported the remote was already connected — it was not (the prior process was gone). An explicit disconnect (which dumped the stale id) followed by reconnect produced a fresh bridgeSessionId and everything worked. The operator has also repeatedly seen ghost sessions on the phone after restarting sessions — old entries persisting in the remote session list, even under the same remote name. Both symptoms point the same way: stale bridge state from a closed process is treated as live by both the client and the account-side session list, and a resumed session inheriting that state at startup is the configuration in which the inbox socket fails to bind.