Repository navigation
[Bug]: A held queued continuation run jams the Claude adapter's continuation gate: later wakes are swallowed, wake-turn output is never recorded, and background subagents stay Running #20430
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
Not reproduced deterministically; this is a reconstruction from one thread's statev2.sqlite, provider event log and boot-service.log on 0.0.46-nightly.20261015.3145 (server up since 2026-10-15 08:02 UTC, never restarted). The sequence that produced it:
- A Claude thread runs background subagents (
Agent,run_in_background: true) and ends its turn. Their completions produce wake continuations, which run normally for hours (claude-wake-turn-detectedis logged 12 times for the thread). - At 19:57:56 UTC a wake continuation (run 31) completes. At 19:57:57 the adapter logs
claude-wake-turn-detectedtwice; the second offer is dispatched as run 32 (message "Background task completed.") and lands inqueued, since run 31 was still finalizing. - Run 32 never starts: its projection row carries
queueHeld: true, socanStartQueuedRunexcludes it on every later run finalization, andneedsRecovery("runtime")excludes held queued runs too. User prompts (runs 33 to 50) start and complete normally around it. The UI shows nothing about a held queue on this thread. - From this point on,
claude-wake-turn-detectedis never logged for the thread again (other threads keep getting wakes all day).
Expected behavior
A continuation run that cannot start should be dropped and the adapter's continuation request released, so the next wake can be offered again. A queue hold on a server-generated continuation message should not need a user to click resume. Background subagents whose completion arrives while the thread is idle should end in the projection.
Actual behavior
The Claude adapter's requestedContinuations entry for the native thread (bufferWakeMessage, the "sticky offer") is only cleared when a continuation turn drains the wake buffer, or on process loss. With run 32 held, the entry stayed set for the rest of the process's life. Everything after that follows from the one stuck bit:
- Every wake turn the CLI ran while the thread was idle was invisible to T3. Between run 38's end (21:12:30 UTC) and run 41 (22:50:39 UTC) the CLI ran wake turns with assistant output, a
SendMessagethat resumed one subagent (21:17:56) and a newAgentlaunch (21:19:10). T3 recorded 26 events in that window, all subagent progress and roster updates: no run, no items. - A background subagent (
task:ac1d42bf0362bfc4c, run 38) completed at 21:18:21 UTC; the SDK deliveredtask_updatedandtask_notification {status: completed}10 ms apart, followed by the wakeinit. The notification was buffered for a continuation that never came, so the row stayedrunning(last update 21:12:06). A sibling subagent that completed 65 s earlier (21:17:16) was applied at once through the settled-turn path, so the idle path itself works; what broke is what happens once the wake buffer holds atask_startedor an earlier frame for the thread. - The idle release deferred twice (
driver-session.idle-release-deferredat 22:01 and 22:31 UTC) because the adapter reported pending background work for subagents it could no longer finish. - When the CLI process finally exited (parent idle),
finalizeActiveTurnAfterQueryExitended only workflow subagents ([Bug]: A background Claude subagent stays running forever when the Claude process exits while its parent is idle #19440) andclearWakeStateForNativeThreaddropped the buffer, buffered completion included. The row stayedrunningfor a day; startup recovery would cancel it (needsRecovery("runtime")includes threads with an open native subagent), but the server never restarted.
Two defects, as far as I can tell from main at 339179f:
ProviderContinuationServicedispatches the adapter-buffered continuation withqueue_after_active, andOrchestrator.startNextQueuedRuncan hold that queued run (queueHeld: true) like a user message. A held continuation is dead weight: there is no user to resume it, and nothing cancels it.- Claude's
continuationRequests.offersets neitherclearIfCurrentnordispatchIfCurrent, so the orchestrator has no way to release the adapter'srequestedContinuationsentry when the continuation it guards is held, cancelled or dropped. fix(server): Claude background subagents end when their wake can't run on Claude #17109 addsclearIfCurrentfor the provider-switch drop in [Bug]: Claude background subagents stay Running forever when their wake continuation runs on another provider #17099; the same hook would need to fire when a queued continuation run is held or cancelled. Once the entry is released, the next wake evidence re-offers, and a continuation that finds an empty buffer already settles immediately.
Related: #19440 and #19464 (ending subagents when the process exits, which would have bounded the damage here), #18910 (a completion that arrived but was not applied; the mechanism above may be one way that happens), #17099 and #17109 (the same sticky entry, different trigger).
Impact
Major degradation or frequent failure
Version or commit
0.0.46-nightly.20261015.3145; main at 339179f has the same paths
Environment
Linux (Ubuntu, systemd user manager), web client, Claude Code CLI on the claudeAgent driver (instance claude-public-pool, model claude-fable-5-1); background subagents on claude-sonnet-5-5
Logs or stack traces
# orchestration_v2_projection_runs, the thread's runs 31-33 (UTC)
31 completed 19:57:56.927 -> 19:57:56.956 (wake continuation)
32 queued 19:57:57.026 -> (never) text "Background task completed." queueHeld=true
33 completed 20:07:37 -> 20:09:30 (user prompt; later user runs 34-50 also start normally)
# boot-service.log, claude-wake-turn-detected for this thread: 12 entries up to 19:57:57.018, none afterwards
[12:57:56.904] INFO orchestration-v2.claude-wake-turn-detected threadId: '618a5f71-...'
[12:57:57.018] INFO orchestration-v2.claude-wake-turn-detected threadId: '618a5f71-...'
# provider event log, the lost completion (UTC)
21:17:16.343Z system/task_updated task_id=a200992c5c0dd05ec patch={status:"completed"} -> applied at 21:17:16.346Z
21:17:16.343Z system/task_notification task_id=a200992c5c0dd05ec status=completed
21:17:16.475Z system/init (wake turn, swallowed)
21:17:56.672Z system/task_started task_id=ab0d0ecdbe27a93d7 tool_use_id=toolu_01YWyvSD (SendMessage resume, root idle, no run)
21:18:21.737Z system/task_updated task_id=ac1d42bf0362bfc4c patch={status:"completed"}
21:18:21.747Z system/task_notification task_id=ac1d42bf0362bfc4c status=completed -> never applied
21:18:21.748Z system/background_tasks_changed tasks=[ab0d0ecd]
21:18:22.007Z system/init (wake turn, swallowed)
# orchestration_v2_projection_subagents, a day later
node:provider:claudeAgent:native-item:task%3Aac1d42bf0362bfc4c status=running started_at=2026-10-15T21:02:40.846Z updated_at=2026-10-15T21:12:06.049Z completed_at=NULL
Screenshots, recordings, or supporting files
No response
Workaround
Cancel the held continuation from the thread's queue (t3_queue_cancel over MCP, or the queue UI). The adapter's entry is per process, so a later CLI process starts clean. The stale subagent row clears at the next server restart.
Activity
juliusmarminge commentedon Oct 17, 2026
Note
Grok responding on behalf of Julius.
Confirmed on
main(0912b24). This is a real gap that the two open PRs don't fully close, so it stays open.ProviderContinuationServicedispatches the Claude wake as aqueue_after_activemessage. A queued run inheritsqueueHeldwhenever another held queued run exists (Orchestrator.ts,message.dispatch), andstartNextQueuedRun/canStartQueuedRunskip a thread with any held run. fix(server): Claude background-task wakes start when the queue is held #17885 stops provider wakes from inheriting a hold, but by its own description it leaves the latch alone.continuationRequests.offerpasses noclearIfCurrentordispatchIfCurrent.requestedContinuationsis cleared only when a continuation turn attaches, or byclearWakeStateForNativeThread. fix(server): Claude background subagents end when their wake can't run on Claude #17109 adds those hooks, but only calls them on an archive, a provider switch or a failed dispatch. Nothing calls them when a queued wake is held or cancelled.queued-run.cancelhas no continuation hook either, and Stop cancels queued provider wakes, so it leaks the latch the same way.One correction to the workaround: cancelling the held run in the queue does not release the latch. Wakes come back only when a new Claude CLI process starts.
One question: before run 32, did this thread already have an older held queued run, for example one held by startup recovery at 08:02 UTC? That tells us whether #17885 alone would have prevented the hold in your case.
Related: #17099 (same latch, provider-switch trigger), #19440 / #19464 (ending subagents when the process exits), #18910 (possibly the same mechanism).