Desktop app: isRunning latches on after a mid-stream message, leaving idle sessions marked "running"
Summary
Sending a message while a turn is still streaming can leave a session's sidebar status dot stuck
on "running" indefinitely, even though the session is idle, the message was received and answered
normally, and the app itself has already recorded that nothing is queued.
The app's own log names the condition (ids redacted):
[LocalSessionManager] isRunning held by unechoed input at result for local_<session>
{ resultUuid: '<uuid>', pendingEchoUuids: [ '<uuid>' ],
nextCycleUuid: null, hasPendingCycle: true, inputStreamHasPending: false }
Analysis
At result (turn end) the manager checks whether deferred input has been echoed into the
transcript. If not, it holds isRunning = true so the dot does not flicker idle before the queued
turn begins. Reasonable in itself - but the check appears to be evaluated once, with nothing
re-evaluating the hold afterwards, so it behaves as a one-way latch.
The decisive evidence: for every occurrence, the uuid the manager reported as unechoed is present
in the session transcript, as an ordinary type: "user" / userType: "external" record carrying
exactly that uuid - and its timestamp falls in the same wall-clock second as the log line that
called it unechoed. Five occurrences over a week of logs, with the transcript record's sub-second
offset ranging from .517 to .933. The app log has second granularity, so I can't give an exact
delta; either reading is a bug:
- the record appended just after the check ran, and nothing re-checked; or
- the record was already present and the check failed to match it.
Also worth noting: the same log line already carries nextCycleUuid: null andinputStreamHasPending: false, i.e. the manager has the information that nothing is pending. The
hold seems keyed purely off the pendingEchoUuids list, so those fields never release it.
Both input paths latch, so the trigger looks like timing rather than input source: three of the
five occurrences were hand-typed mid-stream messages, and two were injected artifact-comment
wake-ups ([Artifact comment sent to Claude]).
Steps to reproduce
- Send a message in a session; while the assistant is still streaming, send a second message.
- Let the turn finish normally. The second message is picked up and answered as expected.
- The sidebar dot for that session stays on "running" (hover tooltip: "running") indefinitely.
- The session's
.jsonltranscript shows no writes for many minutes while the dot still says
running. The session-management MCP tool get_session likewise reports isRunning: true.
A faster variant that reproduced first try: type a message, then immediately hit stop.
Expected
isRunning drops to false once the echo record appends - or once inputStreamHasPending is false
and no cycle is pending.
Actual
It stays true until the session is interrupted or the app is restarted. The hold appears to live inLocalSessionManager memory, so it does not survive a restart, but it also never clears on its own.
Impact
Cosmetic, but it erodes the sidebar's usefulness: with several sessions latched, the dots stop
signalling anything, so a genuinely-running session becomes indistinguishable from an idle one at a
glance. No resource cost - all CLI processes were children of the app's main process, none orphaned,
no background work in flight.
Workarounds
- Send anything in the affected session and immediately hit stop. The interrupt path seems to be the
only in-app reset.
- Restart the app.
Suggested fix
Re-evaluate the hold when a transcript record with a pending uuid appends, rather than only atresult. Cheap belt-and-braces addition: treat inputStreamHasPending === false && nextCycleUuid as sufficient to release, since the manager already logs both.
=== null
Environment
- Claude desktop app
1.37937.1.0; also present on1.34493.1.0. My log only covers one week, so
I can't say when it was introduced.
- Claude Code CLI
2.1.246 - Windows 11 Pro 10.0.22631, session over RDP (mentioned in case latency is relevant to the race)
- Diagnostic:
grep "isRunning held by unechoed input" "%LOCALAPPDATA%\Claude\Logs\main.log"