anthropics / anthropics/claude-code
[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"
- Vorherrschende Sprache
- Python
- Sterne
- 145k
- Forks
- 23.1k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
## 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 ; 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 for without running it - settled (hold=false cycle=true next=false)` and `[warn] peer input for 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 for 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.
Beitragsleitfaden
Für dieses Repository ist kein Beitragsleitfaden indexiert
Rechercherichtung
Start by reproducing the send_message flow with an auto sender and bypassPermissions receiver, then inspect LocalSessionManager logs around the 45-second undelivered and 300-second refused events. Compare the desktop behavior with the documented CLI hold rule. Done means the hold is surfaced as an approval dialog or the sender clearly reports HELD with its reason and expiry.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- macos
- Bereich
- authorization, desktop
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 48/100