[BUG] Completed subprocess/subagent work can leave visible task state stuck in_progress; follow-up prompts no-op
Summary
A Claude Code session can get stuck after long-running subprocess or subagent work completes: the underlying work has finished, but the visible task/todo state remains in_progress / running, and subsequent user prompts cause Claude Code to think briefly and then stop without recovering.
This looks like a broader lifecycle synchronization issue rather than a problem with one specific command or workflow. The common shape is that a subprocess, background task, remote command, or subagent appears to finish, but Claude Code does not reliably reconcile that completion back into the task/todo state that drives the session.
Related issues that seem to describe adjacent symptoms:
- #44783: parent session deadlocks when subagent tool execution hangs
- #48312: completed background Bash tasks stay \"Running\" in Tasks sidebar
- #55893: background Bash tasks get stuck post-completion/across sessions
- #58637: zombie \"running\" background subagents cause stop-hook loops
- #59900: stale task list /
in_progresstasks leak into future context
Expected behavior
When subprocess, background, remote, or subagent work reaches a terminal state, Claude Code should reliably reconcile that state into all relevant layers:
- process / task runner state,
- UI task state,
- model-visible task/todo state,
- stop-hook or session lifecycle state.
If reconciliation fails, a follow-up prompt should still allow the session to recover by checking task status, clearing stale state, or surfacing a clear error/recovery path.
Actual behavior
The underlying work reached a terminal state, but Claude Code continued to show the related task as in_progress / running.
Follow-up prompts did not recover the session. Claude Code appeared to think briefly and then stopped without running commands, updating todos, marking the task complete, or explaining what stale state was blocking progress.
Generalized evidence pattern
The specific workload is not important here and may contain private project details. The relevant evidence pattern was:
- The underlying subprocess / remote command / subagent had already reached a terminal state.
- Completion could be verified independently outside the visible Claude Code task state.
- Expected output or completion side effects were present.
- No corresponding live worker process was observed for the completed work.
- Despite that, Claude Code's visible task/todo state still showed the associated item as
in_progress/ running. - Subsequent prompts did not trigger a reconciliation pass or provide a way to clear the stale state.
This suggests the issue is likely in the completion-event propagation or reconciliation path between task execution, background/subagent lifecycle state, UI state, and model-visible todo/task state.
Why this may be broader than existing reports
Many related issues appear to involve one layer of this same class of problem:
- stale Tasks sidebar entries,
- background Bash tasks that cannot be stopped or queried after completion,
- subagent registry entries that remain marked running,
- stop hooks that believe completed work is still active,
- old
in_progresstask state leaking into later context.
This report is about the cross-layer version: completed work leaves stale visible in_progress task state, and the next user prompt no-ops instead of reconciling or surfacing a recovery path.
Suggested improvements
A robust fix likely needs one or more of:
- A single source of truth for task/subprocess/subagent lifecycle state, or explicit reconciliation between the existing registries.
- A bounded recovery path when completion events are dropped or arrive out of order.
- A session-level reconciliation pass when the user sends a follow-up prompt and visible task state conflicts with process/task-runner state.
- A user-accessible way to inspect and dismiss stale
in_progress/ running entries that no longer correspond to live work. - Clear terminal states such as
completed,failed,stale_orphaned, oracknowledged, rather than leaving stale entries permanentlyin_progress.
Environment
- Claude Code version observed locally while preparing this report: 2.1.139
- Affected session version may differ; this issue appears related to reports across multiple recent versions.
- Platform: observed around long-running subprocess / remote / subagent-style work.
5 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
+1, hitting this from an external orchestrator that spawns
claude -pon an interval. Two adjacent data points that may help isolate which layer is dropping state:in_progresstask as if it were still live. Smells like there's a session-state file that's written async after the process exits, and a follow-up invocation that races it sees the stale state.The "stale
in_progressleaks into future context" sub-issue (#59900 you linked) is the version of this that bites hardest in our workload because we have agents reading task state to decide what to do — a stuck-in-progress task can make the next cycle think the prior cycle is still running and skip its own work. We've worked around it by treating Claude's task state as advisory only and keeping authoritative status in our own state store, but that obviously isn't a fix.Suggestion #4 (user-accessible way to inspect/dismiss stale
in_progressentries) would solve 80% of the operational pain on our side — a CLI command to list and force-close stale entries would be enough to unblock cycles without a/clear.Thanks, this is a really useful data point.
Your subprocess-exit-vs-task-state-update observation matches my case pretty well. In my case, the issue appeared after I opened another Claude Code window in the same working directory. So the common pattern may be multiple Claude Code invocations touching the same project/session state close together, rather than one specific background command or workload.
The async state-write / stale session-state race hypothesis sounds plausible: the underlying work has already reached a terminal state, but a later Claude Code invocation still sees the previous
in_progresstask as live.The stop-hook signal is also interesting. I did have Claude Code hooks configured, so it may be worth treating hook ordering as part of the suspected failure surface — e.g. whether stop hooks run before task-state reconciliation is fully persisted, or whether slow/failed hooks can prevent reconciliation from completing.
I agree with your point on suggestion #4. Even if the underlying lifecycle race takes longer to fix, a user-accessible CLI recovery path would make this much less painful:
in_progressbut have no live backing process/subagent,stale_orphaned/acknowledged.That would let users recover without
/clear, killing the session, or manually editing state.Good write-up — this cross-layer state bug is a real pain for anyone running Claude Code headlessly or overnight.
The symptom you describe (completed work, stale
in_progressstate, follow-up prompts that no-op) is a specific instance of a broader coordination gap: once a subprocess or subagent terminates, there's no out-of-process observer that can authoritatively reconcile the lifecycle state and unblock the orchestrator. The related issues you linked (#58637's zombie stop-hook loop, #55893's stuck background tasks) all stem from the same root: Claude Code's state reconciliation runs inside the session that is already confused, so there's no recovery path from within.I've been building a polling-based orchestration runner for exactly this failure class. The key design invariant: cycle state lives outside the agent process in a durable file store, and a separate heartbeat/renewal task owns liveness tracking. If an agent process dies or gets stuck, the runner can detect the timeout, mark the cycle as
aborted, and let a human trigger a safe retry with the samecycle_id(idempotent re-run). It can't fix the underlying Claude Code state-sync bug, but it does mean a stuck session doesn't silently consume quota until context exhaustion.Happy to share the relevant parts of the state/lock design if useful. In the meantime — upvoted, and the request for a
reconcile_task_state()recovery path is exactly right.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.