Windows (MSIX): cross-session messaging is one-way — peer discovery does not scan `\\.\pipe\LOCAL\`, where the desktop app's pipe lives

Status Open
Reported on v2.1.241
Maintainer reply None cached
Activity 1 comment · opened Aug 25, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

On Windows, a session running in the MSIX-packaged desktop app can send cross-session messages to local CLI / VS Code-extension sessions, but those sessions can neither see it in ListAgents nor address a reply to it. Messaging is one-way, silently.

The cause is the named-pipe namespace. The desktop app is an MSIX package and creates its messaging pipe in the session-local namespace; every CLI/VS Code session creates its pipe in the flat namespace. The CLI side has no notion of the LOCAL\ namespace at all, so the desktop session is invisible to it and its address is rejected as malformed.

Environment

| | |
|---|---|
| OS | Windows 11 Pro 10.0.26200 |
| Desktop app | 1.37937.0.0 (MSIX, C:\Program Files\WindowsApps\Claude_1.37937.0.0_x64__pzs8sxrjxfjjc) |
| Desktop CLI build | 2.1.241 |
| VS Code extension | anthropic.claude-code 2.1.245 installed; peer sessions running 2.1.223 / 2.1.235 |
| Peers present | 15 local CLI/VS Code sessions + 1 desktop session, all in one repo |

Evidence

1. Pipe namespace split. Enumerating named pipes on the machine while all 16 sessions were live:

15 x  \\.\pipe\cc-msg-<hash>              <- every CLI / VS Code session
 1 x  \\.\pipe\LOCAL\cc-msg-<hash>        <- the desktop session, and only it

The desktop session's own registry entry in ~/.claude/sessions/<pid>.json confirms it:

{ "entrypoint": "claude-desktop",
  "messagingSocketPath": "\\\\.\\pipe\\LOCAL\\cc-msg-<hash>" }

while every VS Code entry has "entrypoint": "claude-vscode" and a flat \\.\pipe\cc-msg-… path.

2. Asymmetric visibility, with exact arithmetic. From the desktop session, ListAgents listed all 15 peers. From a VS Code session, ListAgents listed 14 — every local session except the desktop one. 15 flat pipes minus itself equals 14; the desktop session is the only one missing, matching the namespace split exactly.

3. Direct addressing is rejected. The incoming wrapper the peer received carried

<cross-session-message from="uds:\\.\pipe\LOCAL\cc-msg-<hash>" from-name="<desktop-session>" …>

Passing that from value back to SendMessage failed with "not a local socket address". Passing the from-name failed as an unknown recipient, and SendMessage's own error text then proposed three unrelated peers as alternatives — see the section below. The peer declined to guess and escalated to its user instead.

4. Updating the extension does not help. In claude.exe for extension builds 2.1.235 and 2.1.245 (the newest published):

"not a local socket address"   -> present in both
"pipe\LOCAL"                   -> absent from both

The CLI has no code path that constructs or accepts a LOCAL\-namespace pipe path, so this is not fixed by moving peers to the current extension.

What Should Happen?

Either:

  • The CLI/VS Code peer discovery and address parsing should accept pipes under \\.\pipe\LOCAL\ (a normal Win32 path that a non-packaged process can open), so desktop sessions are listed and addressable like any other peer; or
  • If the desktop session is genuinely unreachable from outside the package, ListAgents on the desktop side should not present those peers as two-way, and an unreachable sender should surface a clear reason rather than a generic parse error plus fuzzy name suggestions.

Impact

This is the exact path Anthropic is steering users toward: install the desktop app on a machine that already has CLI/VS Code sessions, then use cross-session messaging. In that configuration the feature is one-way against every pre-existing session on the machine.

The failure is silent and expensive to diagnose. A peer asked a question composes a complete answer, writes it as plain text into its own transcript, and the requesting desktop session simply never hears back — there is no delivery error on either side. Recovering the answer required reading the peer's ~/.claude/projects/<slug>/*.jsonl directly.

Steps to Reproduce

  1. On Windows, run several Claude Code sessions from the VS Code extension in one repo.
  2. Install the desktop app (MSIX) and open a session in the same repo.
  3. From the desktop session, ListAgents — all VS Code peers are listed. Send one a message; it arrives.
  4. In that VS Code session, ListAgents — the desktop session is absent.
  5. Have it reply using the from attribute from the wrapper — rejected as "not a local socket address".

Optional confirmation, read-only:

[System.IO.Directory]::GetFiles("\\.\pipe\") | Where-Object { $_ -like "*cc-msg*" }

Exactly one entry should sit under LOCAL\, and it is the desktop session.

Additional problem: an unreachable recipient comes back with near-name suggestions attached — a misdelivery path built into the mechanism

This is separable from the namespace bug — split it out if you prefer — but it is what turns the namespace bug from an annoyance into a risk of sending conversation content to the wrong session, so it seems worth stating here.

When the peer tried to reply, SendMessage did not report "that session exists but is not addressable from here". It reported the recipient as not found, and then volunteered three alternative recipients:

No agent named '<desktop-session>' is reachable. Did you mean: <peer-1>, <peer-2>, <peer-3>?
Use ListAgents to see everyone you can message.

Three things make this worse than a plain error.

1. The suggestions come from the product, not from the agent. In claude.exe the resolver's nearest-name list is interpolated straight into the failure message:

let k = p.closest.length > 0 ? ` Did you mean: ${p.closest.map(...).join(", ")}?` : "";
…
message: `No agent named '${e.to}' is reachable.${k}…`

The agent did not go looking for candidates; it was handed them at the moment of failure.

2. In this namespace the suggestions cannot carry information. Peer names are derived from the working directory, so every session in one repo shares a prefix. Here the sender's name was one edit from the first suggestion and two edits from at least eight other live peers; the error named two of those eight, arbitrarily. The suggester will always have confident-looking candidates available, and none of them are evidence of identity.

3. The resolver already distinguishes "could not search" from "does not exist" — but this case lands in the wrong bucket. The same code path has a localUnavailable state with its own wording ("The sessions on this machine could not be listed just now, so they were not searched…"). Because the desktop session's pipe is outside the searched namespace, it is not classified as unlisted or unreachable; it falls through to plain not-found, which is exactly the state that triggers suggestions.

So a delivery failure caused by a transport the recipient cannot reach is answered with a plausible-looking list of wrong recipients, and the correct response — stop and escalate — is the one the message argues against. Leaking conversation content into an uninvolved session is one discretionary decision away, every time.

In this instance the peer did stop: it judged that a one-character-different name is a fuzzy match rather than evidence of identity, that sending would deliver a status report to an uninvolved session, and that it would not satisfy the request anyway. But that was the agent's judgment, not a guarantee from the product — and #84768 records the same situation resolving the other way, with the reply silently delivered to an unrelated third session.

Suggested: suppress near-name suggestions when the failure is a transport/enumeration failure rather than a genuine miss, and give the "exists but is not addressable from this session" case its own message, in the same way localUnavailable already has one.

Related

  • #84768 — same user-visible symptom (recipient can't resolve/reply to sender, asymmetric ListAgents) but reported on macOS with a different proposed mechanism (from-name vs from). In this Windows case the from attribute was present and routable-looking; it was the namespace that made it unusable. Possibly the same symptom class with a platform-specific cause on each side.
  • #73543 — local stdio MCP server fails with "access denied for socket file" when spawned by the MSIX-packaged desktop app. Same family: MSIX sandboxing breaking local IPC that works for non-packaged builds.
  • #89563 — WSL2/WSLg cross-session messaging silently disabled by a socket-directory check. Analogous shape on another platform: a transport-level gate that disables the feature with no user-visible reason.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗