Skip to content

[Bug]: Claude background subagent completion is overwritten by a late 'running' update, and Stop/cancel can't settle it #22279

Open

Description

@shaneconner

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Related, but with a different trigger or not covering the dismiss gap: #17099 (wake continuation runs on another provider), #18910 (stays Running after task_notification reports completed), #19993 (parent subagent hits a usage limit), #20430 (continuation gate jam).

Area

apps/server

Steps to reproduce

  1. On a Claude thread running on provider instance A (claudeAgent_me), have the agent start background subagents (Agent with run_in_background).
  2. While they are running, the instance hits its usage limit. Move the thread in place to another Claude instance B (claudeAgent_wrenraeconner) with t3_thread_configure, and continue the thread there. Same driver, different account.
  3. Let the subagents finish.
  4. Try to clear the rows that stay Running.

Expected behavior

  • When their work ends, the subagents reach a terminal state, even if the parent thread has moved to another instance of the same driver.
  • If T3 loses the completion anyway, there is a supported way to settle the row (Stop, dismiss, or task_cancel), without restarting the server or editing the database.

Actual behavior

Two provider-native subagents have shown Running in the Lineage panel for more than 62 hours, and their timers are still counting. Both did their work and returned on 2026-10-17. Nothing on the machine belongs to them.

orchestration_v2_projection_subagents rows (redacted):

node:provider:claudeAgent:native-item:task%3Aade21fe3f39a4fcdc | run ordinal 650 | origin provider_native | instance claudeAgent_me | status running | started 2026-10-17T18:38:25.906Z | completed_at NULL | updated_at 2026-10-20T10:38:12Z
node:provider:claudeAgent:native-item:task%3Aae896c94795fd8e2d | run ordinal 668 | origin provider_native | instance claudeAgent_me | status running | started 2026-10-17T19:56:52.782Z | completed_at NULL | updated_at 2026-10-20T10:38:09Z

Timeline (UTC, 2026-10-17):

  • Runs 650 and 668 start the subagents on claudeAgent_me.
  • Run 675, at 21:25, is the first on claudeAgent_wrenraeconner, after the limit move. The thread later moved again, to a third Claude instance.
  • One subagent's last activity was about 19:35, before the move; the other's was about 22:17, after it.
  • Both rows still read running. updated_at keeps advancing, so something touches them without settling them.

Nothing user-facing clears them:

  • Stop on the thread, and settling, do nothing.
  • MCP task_cancel on either id returns task_not_found: "Delegated task node:provider:claudeAgent:native-item:task%3Aade21fe3f39a4fcdc does not belong to thread 35fd672a-…". It only accepts app-owned delegated tasks.
  • MCP t3_workflow_stop with the subagent id returns invalid_request: "This workflow is no longer running." So the server knows nothing is running, but the projection still says running.
  • TaskStop / SendMessage from the current Claude session can't reach them, because they belonged to the earlier session and instance.

Impact

Minor bug or occasional failure

Version or commit

0.0.46-nightly.20261013.3042 (Arch t3code-nightly-bin)

Environment

Arch Linux (kernel 7.0.12), desktop app. Claude Agent SDK provider with several Claude account instances (claudeAgent_*). Parent claude-opus-5-5; subagents claude-fable-5-1 and claude-opus-5-5.

Workaround

None supported. #17099 reports that server-restart recovery (ProviderRuntimeRecoveryService) cancels such rows. A restart interrupts every other running agent, though, so it isn't usable mid-work.

Suggestion: when t3_workflow_stop or Stop finds a provider-native subagent that is "no longer running", settle its row instead of refusing. Or let task_cancel accept provider-native subagent ids.

Activity

  1. changed the title [-][Bug]: Claude background subagents stay Running forever after the thread moves to another Claude instance, and nothing can settle them[/-] [+][Bug]: Claude background subagent completion is overwritten by a late 'running' update, and Stop/cancel can't settle it[/+] on Oct 20, 2026
  2. shaneconner commented on Oct 20, 2026

    @shaneconner
    Author

    Update: root cause found. The instance move in the title was not the cause. I read the event log and the source at 5d6e4192 (the commit this nightly was built from). Both subagents did complete on time, and a late running update from the same session overwrote each completion seconds later. Both completions happened before the thread moved instances at 21:25.

    Event log (orchestration_events, subagent.updated for each node):

    node seq status note
    task%3Aade21fe3f39a4fcdc 1522108 completed completedAt 2026-10-17T19:01:02Z
    1522128 running +17 s; stale startedAt, result nulled, completedAt null
    task%3Aae896c94795fd8e2d 1537176 completed completedAt 2026-10-17T20:07:02Z
    1537238 running +35 s; same pattern

    There is no node.updated or new task_started between them, so these aren't resumes. It looks like a stale in-flight progress update landing after the completion.

    Why it sticks:

    • The projector writes subagent.updated without checking state: apps/server/src/orchestration-v2/ProjectionStore.ts:2058 (the UPSERT). A terminal row can go back to running.
    • The adapter's terminal guards live in its in-memory registry (ClaudeAdapterV2.ts:4863-4870, 5024-5038), so they don't protect the persisted row.
    • subagent.stop re-emits the record with only updatedAt bumped (Orchestrator.ts:9019-9031). That's why Stop "succeeds" and keeps advancing updated_at while the row stays running.
    • Only server-restart recovery settles such rows (ProviderRuntimeRecoveryService.ts:470-483).

    Suggested fixes:

    • Never let a non-terminal subagent.updated replace a terminal row in the projector, and do the same check in the adapter before emitting.
    • Have subagent.stop settle the row when the provider reports the task is no longer running, rather than only bumping updatedAt.

    I settled our two rows by appending subagent.updated and turn-item.updated events in the server's own shape, with the matching projection rows, in one transaction. That held without a restart, and a replay on a copy reproduced the same rows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions