Repository navigation
[Bug]: V2 Claude run stays "running" forever after the CLI reports result/success (thread stuck on "Thinking") #15197
Description
Activity
Update: Stop does not recover the thread.
Stop was pressed 7 times at 14:05:15–14:05:16Z and again at 14:09:21Z. Each press:
- dispatched
run.interrupt, which returnedSuccess; - enqueued a
provider-turn.interrupteffect inorchestration_v2_effect_outbox, which endedsucceededon attempt 1; - ran
ClaudeAdapterV2.interruptTurn, which returnedSuccess; - also dispatched
thread.background-work.settle.
After all of that, the run is still
runningwithcompleted_at = NULL. The Claude CLI process for this thread had already exited, so I think the interrupt has no live turn to cancel. No terminal event ever comes back, and the run never leavesrunning.The user also has a follow-up message queued on this thread. It now sits behind the stuck run and is never sent.
I haven't yet tested whether restarting the app clears the run.
- dispatched
Note
Grok responding on behalf of Julius.
Triage
Thanks @Info-Cado for checking the database state and separating this from the nearby issues. That made this much easier to pin down. From the source, this looks like a real server-side stuck run on the V2 Claude path, not a client refresh glitch and not the checkpoint stall in #15124.
What the rows tell us
A successful Claude result only leaves
runningonceClaudeAdapterV2finalizes the turn. That emitsprovider_turn.updatedandturn.terminal, andwriteFinalRunEventsthen saves the run aswaitingand enqueuescheckpoint.capture(RunExecutionService.ts, around thepersistedStatusassignment). Your run, attempt, and provider turn are all stillrunning, and nocheckpoint.capturewas ever enqueued, so that transition never happened. The provider session'sreadystatus is its default, so it doesn't show the turn finished.Where the result could have been dropped
Seeing
result/subtype: successin the protocol log only proves the adapter read the frame. It can still decline to settle the turn. While the prompt echo is unconfirmed,handleSdkMessagetreats aresultas belonging to another turn when itsuser_message_uuidsdon't include this prompt, or when it has no uuid,promptEchoModeis no longerunknown, andorigin.kindisn'thuman(isClaudeResultForOtherTurn). That path logsorchestration-v2.claude-result-for-other-turnand returns without callingfinalizeActiveTurn. The assistant text is already rendered by then, which fits a finished answer under a timer that never stops. Thecommand_lifecyclecompletedframe afterwards doesn't settle anything, and the foregroundlocal_bashtask isn't background work, so it doesn't explain this.Stop can hit the same gap.
provider-turn.interruptcallsinterruptTurnwithrequestRuntimeRestart: true. If the in-memory active turn is already gone, it returns success without emittingturn.terminal, so the outbox row can succeed while the run staysrunningand any queued follow-up stays blocked behind it.Related
- [Bug]: ⚠️ Claude provider: every turn gets stuck showing "Working..." forever after the CLI already completed — stop button doesn't recover it #4452 (closed) is the same symptom on the V1 Claude path.
- [Bug]: New thread sometimes stays on "Thinking" with a spinning send button after a quick first turn until reload #14046 (open) is the client-only case, where a reload shows a turn the server already stored.
- [BUG] V2 waiting run keeps showing Agent is working and offers Stop for finished work when checkpoint capture stalls #15124 (open) is a run that did leave
runningand is stuck inwaitingoncheckpoint.capture. - fix(server): runs no longer get stuck #15048 (merged after
fed41fa88bb2) fixes other stuck-run causes: a failed read at turn start, rollback ordering, a failed session release, and client event order.ClaudeAdapterV2.tshasn't changed since that nightly, and there's no open PR for this path.
What would help
A few fields from the logs would show which branch dropped the result. Please redact prompt text and home paths:
- From the
resultframe:originanduser_message_uuids/user_message_uuid. - Whether any earlier
command_lifecyclefor this prompt uuid arrived before the result. - Whether the server trace has
orchestration-v2.claude-result-for-other-turnororchestration-v2.claude-task-notification-result-droppedaround2026-10-03T13:40:46Z.
If you restart the app, it would also help to know whether that run row is still
runningafterwards.A maintainer will decide on the fix direction.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.needs more infoInitial triage showed no bug. Awaiting more infoInitial triage showed no bug. Awaiting more infovia-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 3, 2026 Thanks @juliusmarminge. Here is what I could get. It comes from the provider event log and
statev2.sqlite. Prompt text and home paths are redacted.1. The
resultframe{ "type": "result", "subtype": "success", "uuid": "cfc2ef2d-…", "user_message_uuids": ["ed0f6284-…"], "user_message_uuid": "ed0f6284-…", "num_turns": 9, "queued_turn_count": 0, "result_index": 0, "stop_reason": "end_turn", "terminal_reason": "completed", "is_error": false }- The frame has no
originfield. I checked the whole raw payload. ed0f6284-…is theuuidof this turn'sprompt.offer(13:39:55.230Z). So the result names this turn's own prompt.
2.
command_lifecyclebefore the resultYes. Both arrived about 50 s before the result:
Time (Z) Frame 13:39:55.230 outgoing prompt.offer, uuided0f6284-…13:39:55.991 command_lifecyclequeued,command_uuid: ed0f6284-…13:39:57.028 command_lifecyclestarted,command_uuid: ed0f6284-…13:39:57.054 system/init13:40:00.844 → system/thinking_tokensframes carryinguser_message_uuid: ed0f6284-…13:40:38.376 / .848 task_started/task_notification(local_bash,is_backgrounded: false,completed)13:40:46.593 result(above)13:40:46.617 command_lifecyclecompleted,command_uuid: ed0f6284-…No
userreplay frame echoed the prompt. The onlyuserframes are tool results.3. Server trace
I can't check this any more. The server trace has rotated, and the oldest span left is from about 18:25Z, hours after 13:40:46Z.
orchestration_v2_eventsis also empty, so the DB has no event-level history to fall back on. Sorry.New evidence: the adapter had already cleared its active turn
I missed this in the first report.
orchestration_v2_effect_outboxalso has aprovider-turn.steerrow. The queued follow-up was sent at 13:51:20Z, about 10 minutes after the result. It was routed as a steer into run 1, and all 5 attempts failed:OrchestrationEffectExecutionError [cause]: ProviderTurnControlError [cause]: ProviderAdapterSteerRunError: Failed to steer active run provider-turn:…ordinal%253A1%3Aattempt%3A1 on claudeAgent provider thread … [cause]: ProviderAdapterProtocolError: claudeAgent provider protocol error: Claude provider turn provider-turn:…ordinal%253A1%3Aattempt%3A1 is not the active turn.At
fed41fa88bb2,steerTurnreturns "has no live query" whenqueryContextis null. So the CLI query was still live at 13:51. It returns "is not the active turn" whenactiveTurndoes not hold this provider turn. As far as I can tell,activeTurnis written in only two places:Ref.set(activeTurn, context)instartTurn. No otherprovider-turn.startran on this thread between 13:39:55Z and 14:10:14Z.- The
Ref.update(activeTurn, …→ null)at the end offinalizeActiveTurn. It only runs after theprovider_turn.updated,provider_thread.updatedandturn.terminalemits have returned.
If I'm reading that right, the adapter did finalize this turn. The result was not dropped by
isClaudeResultForOtherTurn, and it was not the task-notification drop either. Both of those return early and leaveactiveTurnset, so the steer would have found the turn. Yet the provider turn, attempt and run all stayedrunning, and nocheckpoint.capturewas ever enqueued. That points to a loss afteremitProviderEvent, somewhere between the adapter and the projection orwriteFinalRunEvents, rather than in the adapter's result handling.This would also explain the lost follow-up. The run still looked
running, so the message went to the adapter as a steer, and a turn that is already finalized can't accept one.After restarting the app
The app restarted at about 14:10:07Z to install an update (now
0.0.46-nightly.20261003.2632). After that:- Run ordinal 1 became
cancelled, withcompleted_at = 14:10:07.924Z. Its provider turn iscancelledtoo. - A
provider-runtime.continueeffect (sourceRunId= run 1) started run ordinal 2 on a resumed Claude session, with the prompt "Continue where you left off." It completed normally at 14:10:32Z and enqueuedcheckpoint.capture. - The queued follow-up was never delivered. Its text doesn't appear in any
prompt.offerin the provider log. It is still stored as a user message attached to run 1. - The next message (run ordinal 3) completed normally.
So a restart clears the stuck run, but the queued follow-up is silently lost.
Correction to my earlier update
I said the Claude CLI process had already exited before Stop. That's wrong. The outbox has 16
provider-turn.interruptrows between 14:05:08Z and 14:05:16Z, plus one at 14:09:21Z. The first one was created at 14:05:08.182Z, and the provider log shows the outgoingquery.closeat 14:05:08.196Z. So the first Stop is what closed the CLI. The later presses found nothing to cancel.- The frame has no
- added a commit that references this issue
on Oct 6, 2026
Before submitting
Area
apps/server (orchestration V2, Claude adapter)
Steps to reproduce
Intermittent. Happened once in about 120 Claude runs in my local history.
claude-opus-5-5[1m],bypassPermissions) in a worktree.Expected behavior
When the Claude CLI emits
result/subtype: success/terminal_reason: completed, the run moves tocompletedand the thread goes idle.Actual behavior
The final assistant message renders, but the thread keeps showing "Thinking" and the "Working for Xm Ys" timer keeps counting indefinitely.
The provider log shows the turn ended cleanly at
2026-10-03T13:40:46.593Z. The last two events are:{"type":"result","subtype":"success","stop_reason":"end_turn","terminal_reason":"completed","is_error":false,"num_turns":9,"duration_ms":49562,"queued_turn_count":0,"result_index":0, ...} {"type":"command_lifecycle","command_uuid":"ed0f6284-...","state":"completed", ...}Server state in
statev2.sqlite, checked more than 20 minutes later:orchestration_v2_projection_runs:run:thread:<id>:ordinal:1hasstatus = runningandcompleted_at = NULL.orchestration_v2_projection_run_attempts: attempt 1 hasstatus = running.orchestration_v2_projection_provider_turns: the provider turn hasstatus = running.orchestration_v2_projection_provider_sessions: the session hasstatus = ready.orchestration_v2_effect_outbox: no pending or failed rows. Nocheckpoint.capturewas ever enqueued for this run, so this is not thewaiting/ stalled-capture case from [BUG] V2 waiting run keeps showing Agent is working and offers Stop for finished work when checkpoint capture stalls #15124.Other details:
task_started/task_notificationpair (local_bash,is_backgrounded: false,status: completed) at 13:40:38, about 8 s before the result.command_executionturn item ended asfailedat 13:40:18.Success. I could not find an error.The provider session stays
readywhile the run, attempt and provider turn stayrunning. That suggests the adapter received theresultbut the run-terminal transition was never committed.Impact
Major degradation or frequent failure. The thread looks busy forever, and it is unclear whether the agent is still working.
Version or commit
0.0.46-nightly.20261003.2623(fed41fa88bb2), the latest nightly at the time of writing.Environment
Desktop (AppImage), Linux (Arch-based, Hyprland), Claude provider via Claude Agent SDK, orchestration V2.
Logs or stack traces
The provider event log and the server trace for the thread are available on request. I'm not attaching them because they contain prompt text and local paths.
Screenshots, recordings, or supporting files
No response
Workaround
Press Stop, or restart the app.
Related issues and PRs
running). Closed as fixed by fix(server): stop kills lingering Claude work #5891 and fix(server): reconcile orphaned provider sessions #7719. This report is a V2 recurrence.waiting(checkpoint-pending) state. Here the run never leftrunning.