[BUG][macOS desktop] Cross-session message to a bypassPermissions session is held with no visible approval dialog, expires after dialogExpiry (5 min) and is dropped while the sender is told "queued"

Status Open
Reported on v2.1.258
Maintainer reply None cached
Activity 0 comments · opened Sep 4, 2026

Summary

In the macOS desktop app (Code tab), a cross-session message sent to a session running in bypassPermissions is held for an approval dialog that is never shown, expires after dialogExpiry (5 min) and is dropped. The sender's tool result says the message was queued, so an orchestrating session believes it was delivered. 7 of 7 drops over three nights fit this; every delivered control had an auto receiver.

Environment

  • Claude desktop app, macOS (Darwin 25.6.0), Code tab, bundled CLI 2.1.258; standalone CLI on PATH 2.1.260
  • Sending session: permission mode auto (interactive, entrypoint: claude-desktop)
  • Receiving session: permission mode bypassPermissions, set via the desktop UI (LocalSessions.setPermissionMode ... mode=bypassPermissions in main.log)
  • No crossSessionInbound or dialogExpiry set anywhere (user / project / local settings unset), so the documented default class rule applies

What happens

  1. Session A (auto) calls mcp__ccd_session_mgmt__send_message to session B (bypassPermissions), or SendMessage to B's peer name.
  2. A's tool result: Message queued for session <B>; it will be processed after the in-flight turn finishes if that session stays healthy. (a later variant: has not acknowledged it yet - it may be waiting for approval there).
  3. B's UI shows a card for the incoming message, but B's Claude never sees it: B's transcript .jsonl contains zero occurrences of the text, no queue-operation, no user turn.
  4. ~/Library/Logs/Claude/main.log, +45 s: [LocalSessionManager] CLI undelivered input <uuid> for <B> without running it - settled (hold=false cycle=true next=false) and [warn] peer input <uuid> for <B> drew no acknowledgement from the CLI in 45002ms - settled as undelivered (ownedCycle=true).
  5. main.log, exactly +300 s after the send: [LocalSessionManager] CLI refused input <uuid> for <B> without running it - settled (hold=false cycle=false next=false). That is the dialogExpiry default firing on a dialog nobody could see.
  6. No approval dialog was ever visible in B's desktop window. B was in the foreground for part of the window; a human typed into B at +83 s and B answered that it had received nothing.

Why this looks like the documented hold rule, not a race

The cross-session messaging docs (Control inbound messages) say a receiving session that bypasses permission prompts holds each message for the user's approval and delivers it only when the sending session identifies itself as bypassing too. In the desktop app that approval is never surfaced, so every such message is dropped, while the sender is told it was queued.

Evidence: 7 of 7 drops, all with a bypassPermissions receiver, all refused at +300 s

| send (local time) | receiver | receiver mode | undelivered warn | refused | delta |
|---|---|---|---|---|---|
| 2026-09-02 21:02:06 | session 1 | bypassPermissions (set 20:59:47) | 21:02:51 (45 s) | 21:07:06 | +300 s |
| 2026-09-02 21:04:22 | session 1 | bypassPermissions | 21:05:07 (45 s) | 21:09:22 | +300 s |
| 2026-09-02 21:08:40 | session 2 | bypassPermissions | 21:09:25 (45 s) | 21:13:40 | +300 s |
| 2026-09-02 21:18:36 | session 3 | bypassPermissions | 21:20:06 (90 s, hold=true) | 21:23:36 | +300 s |
| 2026-09-02 21:20:06 | session 3 | bypassPermissions | 21:20:51 (45 s) | 21:25:06 | +300 s |
| 2026-09-02 21:35:13 | session 4 | bypassPermissions | 21:36:43 (90 s, hold=true) | 21:40:13 | +300 s |
| 2026-09-04 04:12:38 | session 5 | bypassPermissions (transcript) | 04:13:23 (45 s) | 04:17:38 | +300 s |

Delivered controls the same nights, every receiver in auto: the ccd bridge to an idle target 6 min after its last turn and 90 s after; SendMessage to an idle target; SendMessage to a busy target, drained at its next tool round. Timing since the target's last turn, idle vs busy, and the choice of bridge do not separate the two groups; the receiver's permission class does.

Expected

Either of:

  • The desktop app surfaces the hold as an approval dialog in the receiving session (as the CLI does in a terminal), or
  • The sender's tool result says HELD, names the reason (receiver bypasses permissions, sender does not) and the expiry, instead of "queued", so an orchestrating session can act (re-route to the human, or stop).

Reproduction (deterministic)

  1. Desktop app, two Code sessions in any folder. Leave settings default (no crossSessionInbound).
  2. Session B: switch permission mode to bypass in the UI. Let it finish a turn and sit idle.
  3. Session A (auto): ask it to send B a one-line message via mcp__ccd_session_mgmt__send_message (or SendMessage to B's peer name).
  4. Watch ~/Library/Logs/Claude/main.log: settled as undelivered at +45 s, CLI refused input at +300 s. B's transcript never gets the text. A was told "queued".
  5. Control: repeat with B in auto: delivered as a new turn within seconds.

Possibly related issues (none describes this exact mechanism)

  • #86012 (closed): recipient wedged 15-20 min with hadFirstResponse=false; different signature (this one acks and refuses at +300 s, no watchdog).
  • #86014 (open, Windows): send_message reports success, 0/4 delivered regardless of target state; no permission-mode analysis. Possibly the same cause unexamined.
  • #86069: message lands in the composer but is never submitted.
  • #86088 (closed as dup of #86012): unfocused-window turns never start.
  • #76714: permission-grant UX for the same tool.

View original on GitHub ↗