[BUG] Claude Code Desktop — turn ends but UI stays "running": timer keeps counting, input blocked, while session list shows the session idle

Status Open
Reported on v2.1.284
Maintainer reply None cached
Activity 0 comments · opened Oct 1, 2026

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?

After a turn finishes, the conversation view keeps showing the running indicator — the spinner plus an elapsed-time counter that keeps incrementing (e.g. 11m 43s · 5.2k tokens) — long after the assistant has stopped producing output.

Three UI surfaces disagree at the same moment:

| Surface | State shown |
|---|---|
| Conversation view (main pane) | Running — spinner + elapsed timer still counting |
| Session list (sidebar) | Idle/complete — the session's status dot is blank |
| Composer (input box) | Blocked — a new message cannot be sent; it waits for the spinner to end |

So the backend appears to consider the turn finished (sidebar is correct) while the front end both keeps the timer alive and blocks input.

Duration and behavior

  • Stall length varies wildly: tens of seconds up to 22+ minutes observed.
  • It eventually clears by itself. The wait seems roughly proportional to how long it was stuck.
  • No tool call, no text, no visible activity of any kind during the stall. The spinner and the counting timer are the only things on screen.
  • Happens frequently. It appears more often when other Claude Code sessions are open at the same time.
  • The only workaround is quitting and restarting the app entirely — which for a long session is costly.

Why this is severe: once it stalls, that session is unusable until it clears or the app is restarted. Because it can happen to any session, having several open means one of them is intermittently frozen.

What Should Happen?

The desktop log records turn lifecycle events. Over 48 complete turns:

turns: 48    median 64.6s    max 3753.7s    min 2.7s

The longest gaps between consecutive log events are almost all bounded by control-channel messages (control_request / control_response), not by model or tool activity. Examples:

1350.0s  control_request/mcp_status  ->  control_response
 459.0s  control_response            ->  control_response
 345.9s  control_response            ->  control_request/interrupt
 300.5s  control_response            ->  control_response
 212.8s  control_response            ->  control_response
 201.7s  control_response            ->  control_response

A representative stalled turn (UTC):

13:41:46.196  cli_turn_started
13:41:47      cli_ask_should_query_resolved
13:41:47      control_request/mcp_status
13:41:47+40s  control_response          <- 40.4s gap
              ...                    <- 548s of no lifecycle events

Observation about where cli_ask_turn_complete is emitted. In the 2.1.284 binary the emission site is:

let Fe = await this.turnRunner.run({ batch, input, tools, intake });   // turn work
let Fr = this.drainSettle.settleBatch({ batch, deliveredPollBatchUuids, turnEnd: Fe, turnSignal });
if (Fr) await Fr;                                                       // settle before signalling
V("info", "cli_ask_turn_complete", { should_query, batch_size, mode }); // completion signal

i.e. the completion signal is emitted only after drainSettle.settleBatch(...) resolves. If that settle does not resolve, cli_ask_turn_complete is never emitted — which would match what is seen: the conversation view never learns the turn is over, while the sidebar (a separate state source) correctly shows idle.

Possible lead, unverified: the call site passes { batch, turnEnd, turnSignal } while settleBatch is defined to destructure { batchUuids, deliveredPollBatchUuids, terminalReason, aborted }. Three parameter names do not line up. This may be a coincidence between two same-named methods on different classes (the code is minified so I cannot confirm), but it is worth a look.

Error Messages/Logs

Steps to Reproduce

Not reliably reproducible on demand — it is intermittent. Best reproduction attempts:

  1. Have more than one Claude Code session open.
  2. Run a long session (the transcript in this environment had grown past 80 MB).
  3. Let a turn complete and observe the conversation view after the assistant stops talking.
  4. Observe the three surfaces disagree: sidebar dot blank (idle), main pane spinner counting, composer refusing input.

Claude Model

Other

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

claude --2.1.284

Platform

Other

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

  • The stall looks confined to the desktop front end / control channel — every piece of evidence above is local (control-channel gaps and UI state); no upstream request or response stream is implicated.
  • mcp_status requests appear at the start of every turn; the first control_response after them is frequently delayed by 16–40s. MCP connection times are much smaller in comparison (duration_ms median 1551 ms, p99 12306 ms, max 21727 ms), so MCP does not account for the multi-minute stalls.
  • 3 of 115 mcp_connect_starting events had no matching completion event.

View original on GitHub ↗