[BUG] Completed subprocess/subagent work can leave visible task state stuck in_progress; follow-up prompts no-op

Status Fixed / completed
Reported on v2.1.139
Maintainer reply None cached
Activity 5 comments · opened May 17, 2026 · closed Jun 1, 2026

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_progress tasks 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:

  1. process / task runner state,
  2. UI task state,
  3. model-visible task/todo state,
  4. 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_progress task 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:

  1. A single source of truth for task/subprocess/subagent lifecycle state, or explicit reconciliation between the existing registries.
  2. A bounded recovery path when completion events are dropped or arrive out of order.
  3. A session-level reconciliation pass when the user sends a follow-up prompt and visible task state conflicts with process/task-runner state.
  4. A user-accessible way to inspect and dismiss stale in_progress / running entries that no longer correspond to live work.
  5. Clear terminal states such as completed, failed, stale_orphaned, or acknowledged, rather than leaving stale entries permanently in_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.

View original on GitHub ↗

5 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/48312
  2. https://github.com/anthropics/claude-code/issues/55893
  3. https://github.com/anthropics/claude-code/issues/58637

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

kcarriedo · 5 months ago

+1, hitting this from an external orchestrator that spawns claude -p on an interval. Two adjacent data points that may help isolate which layer is dropping state:

  1. Subprocess exit vs. task-state update are not atomic. When we instrumented our runner (separate Rust process, spawns Claude as a subprocess), the child process exits cleanly with code 0 and the stdout JSON shows the task closing — but if the parent orchestrator then re-invokes Claude within the same session/working-directory shortly after, the new invocation sometimes inherits the prior session's in_progress task 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.
  1. Stop-hook ordering. We noticed the "follow-up prompt no-ops" symptom triggers far more often when a stop-hook is configured than without one — best guess is the hook completion is gating a state-reconciliation step that doesn't run if the hook is slow or fails silently. Removing the stop-hook (just to test) made the no-op recoveries happen ~10x less frequently. Not a fix, but a useful triage signal.

The "stale in_progress leaks 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_progress entries) 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.

InitialMoon · 5 months ago

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_progress task 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:

  • list current persisted task/todo/session entries,
  • identify entries that are in_progress but have no live backing process/subagent,
  • force-close or mark them as stale_orphaned / acknowledged.

That would let users recover without /clear, killing the session, or manually editing state.

kcarriedo · 4 months ago

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_progress state, 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 same cycle_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.

github-actions[bot] · 2 months ago

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.