Desktop: queued-message bar becomes permanently undismissable — cancelQueuedMessage falls through to "too-late" (31 of 43 cancel attempts in my logs)
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?
The desktop app's queued-message bar ("1 message queued …") can get into a state where it can never be dismissed. Clicking its × does nothing, silently, forever — the main process can't find the message in any of its queues, so cancellation always fails while the UI keeps advertising the message as queued.
This isn't rare. Across my main*.log history, cancel attempts break down as:
→ cancelled 12
→ too-late 31
72% of all × clicks fail. The repeat counts in the logs are me clicking again because nothing happened.
cancelQueuedMessage tries five strategies in order — heldSteers, deferredSends, coalescedDrain constituents, inputStream.remove(uuid), then query.cancelAsyncMessage(uuid) — and logs the aggregate result:
n.logger.info(`[LocalSessionManager] cancelQueuedMessage ${r} → ${s ? "cancelled" : "too-late"}`), s
A bare → too-late (no parenthetical) means every one of the five missed: the uuid the renderer is showing exists in none of the manager's queues. The two more specific variants — too-late (coalesced turn already consumed) and too-late (already drained) — never appear in my logs, so this is the fall-through case, not a genuine race with a turn that already consumed the message.
The renderer treats the failed result as "keep showing the bar", so the desync is permanent for that session. Restarting the app is the only way I've found to clear it (this state is in-memory — heldSteers / deferredSends / coalescedDrain all live on the in-memory session object, and none of it is in the persisted session JSON).
What Should Happen?
A × click should always dismiss the bar. If the message can't be found in any queue, that means the UI is showing something the main process no longer has — the correct response is to clear the bar (the message is already gone), not to leave an undismissable widget. Failing that, the failure should at least surface to the user instead of being an info-level log line.
Error Messages/Logs
The same queued message, stuck across 22 minutes and a session switch, seven × clicks, all failing identically:
2026-07-27 03:32:25 [info] LocalSessions.cancelQueuedMessage: sessionId=local_c83fe0a8…, uuid=e1149a1f…
2026-07-27 03:32:25 [info] [LocalSessionManager] cancelQueuedMessage e1149a1f… → too-late
2026-07-27 03:32:27 [info] LocalSessions.cancelQueuedMessage: sessionId=local_c83fe0a8…, uuid=e1149a1f…
2026-07-27 03:32:27 [info] [LocalSessionManager] cancelQueuedMessage e1149a1f… → too-late
2026-07-27 03:32:28 [info] (×3 more, identical)
…
2026-07-27 03:54:13 [info] [CCD] LocalSessions.setFocusedSession: sessionId=local_c83fe0a8…
2026-07-27 03:54:13 [info] [WarmLifecycle:preview] Warming up session local_c83fe0a8…
2026-07-27 03:54:16 [info] LocalSessions.cancelQueuedMessage: sessionId=local_c83fe0a8…, uuid=e1149a1f…
2026-07-27 03:54:16 [info] [LocalSessionManager] cancelQueuedMessage e1149a1f… → too-late
2026-07-27 03:54:16 [info] (×1 more, identical)
Note the second cluster: the bar came back with the session on re-focus, still showing the same uuid, still uncancellable.
Steps to Reproduce
I don't have a deterministic repro — flagging that honestly rather than inventing one. What I can say about the occurrence above:
- The message was queued while a turn was running, in a session where the queued text was a slash command the session's environment then rejected as unavailable (
/remote-control, in a non-interactive agent session — it printed "isn't available in this environment"). - The bar remained after the turn ended.
- × does nothing, at 03:32 and again at 03:54 after switching away and back.
Two candidate triggers I could not separate with the evidence I have:
- A rejected slash command never acks. If the queued entry left
deferredSendsand was dispatched, but the command was rejected before anything sent a completion lifecycle event back, nothing would clear the bar and nothing would remain to cancel. Consistent with the fall-throughtoo-late, but I have not instrumented the ack path. - Session switch drops the queue entry but not the bar. #77010 and #77451 report queued messages being silently lost across a session switch. This would be the same underlying loss seen from the other side: entry gone, bar still rendered, cancel therefore impossible. Those two issues describe the message disappearing; neither mentions the bar persisting or × failing, so I've filed this separately rather than commenting there.
The 31-vs-12 ratio suggests whatever it is, it's common and not specific to slash commands.
Claude Code Version
Bundled CLI 2.1.219 (desktop app 1.24012.9)
Platform
Claude Desktop app
Operating System
macOS (Darwin 25.5.0, Apple Silicon)
Additional Information
Suggested fix: treat "not found in any queue" as success rather than failure. In cancelQueuedMessage, the fall-through case means the message is not cancellable because it no longer exists — returning false there is what makes the widget permanent. Returning true (or having the renderer clear the bar on any terminal result) would make the × always work, and would be safe: there is nothing left to cancel in that branch by definition.
The genuine-race cases already log distinctly (coalesced turn already consumed, already drained), so they can keep returning false without affecting this.
Code references are from reading the shipped bundle at /Applications/Claude.app/Contents/Resources/app.asar; identifiers are minified, control flow and log strings are verbatim.