[BUG] Task store splits after crash + --resume: idle dispatcher replays stale tasks to subagents as synthetic "task-list" messages

Open 💬 0 comments Opened Jul 9, 2026 by manar-c

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

After a session crash + claude --resume, the task subsystem splits across two store keys:

  • The task TOOLS (TaskCreate/TaskList/TaskUpdate) bind to a fresh per-process key: ~/.claude/tasks/session-<8hex>/ — numbering restarts at #1.
  • The built-in idle/join TASK DISPATCHER stays bound to the original conversation UUID: ~/.claude/tasks/<conversation-uuid>/ — which still holds the entire pre-crash task list. Completions made after the resume go to the new store, so the old store's tasks stay "open" forever.

Consequence: every time a team subagent (spawned via the Agent tool) starts or goes idle, the dispatcher pulls the next non-completed task from the STALE store, marks it in_progress with that agent as owner (writing the stale store's JSON), and messages the agent as a synthetic sender named "task-list":

"Complete all open tasks. Start with task #NNN: <full stale task subject + description>"

The referenced IDs don't exist in the live task list (TaskGet → "Task not found"), and replies to "task-list" bounce ("No agent named 'task-list' is reachable") — it has no team inbox.

Impact observed: subagents received directives for tasks already completed and merged days earlier. One subagent accepted a replayed task and performed substantial unauthorized work (source edits + new files in the repo working tree) before intervention — the directive carried a complete, plausible task description (a real one, just stale), so the only detectable invalidity signal was the ID mismatch against the live TaskList.

What Should Happen?

  • The dispatcher and the task tools should resolve the SAME task store (whichever key is chosen — per-conversation or per-process — just consistently).
  • On --resume, the conversation's existing task store should be adopted/migrated rather than a fresh key created alongside it.
  • The dispatcher should validate a task against the live task list before dispatching it to an agent, and identify itself with a real, replyable team identity.
  • A stale queue should never silently convert tracking entries into executed work.

Error Messages/Logs

# Two stores for ONE conversation (conversation uuid matches transcript filename + scratchpad path):
~/.claude/tasks/<conversation-uuid>/   -> old numbering (#85-#111), stale pending/in_progress statuses
~/.claude/tasks/session-<8hex>/        -> live numbering (#1-#N); matches ~/.claude/teams/session-<8hex>/

# Dispatch message received by subagents (sender "task-list"):
"Complete all open tasks. Start with task #110: Fix Bug 2: ... <verbatim stale description>"

# Reply attempt:
SendMessage -> "No agent named 'task-list' is reachable."

# Smoking gun — stale-store file mtimes match team-agent lifecycle events TO THE SECOND
# (joinedAt values from ~/.claude/teams/session-<8hex>/config.json; idle timestamps from
#  idle_notification messages). One dispatch write per spawn and per idle event:
101.json  mtime 09:26:26  == agent A joinedAt 09:26:26   -> status in_progress, owner "agent A"
102.json  mtime 09:26:55  == agent B joinedAt 09:26:55   -> in_progress, owner "agent B"
...
105.json  mtime 09:34:37  == agent A idle notification 14:34:37.692Z (= 09:34:37 local)
106.json  mtime 09:35:02  == agent B idle notification 14:35:02.859Z
110.json  mtime 09:36:42  == agent D idle notification 14:36:42.331Z

Steps to Reproduce

  1. Start an interactive session in a project; create several tasks with TaskCreate (numbering reaches some #N > 1).
  2. Kill the CLI process mid-session (simulate a crash).
  3. claude --resume and continue the same conversation.
  4. Create a new task — observe numbering restarted at #1 (tools now on a fresh session-<8hex> store; the old <conversation-uuid> store still holds the pre-crash tasks, non-completed).
  5. Spawn a named background subagent via the Agent tool (in-process team).
  6. Observe: at spawn — and again whenever the agent goes idle — it receives a "task-list" message instructing it to complete a stale old-numbered task, and the corresponding JSON in the OLD store is updated to in_progress with the agent as owner.

Workaround that stops it: back up ~/.claude/tasks/<conversation-uuid>/, then set every non-completed task's "status" to "completed". Dispatches cease immediately.

Claude Model

Other

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.205

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Xterm

Additional Information

Environment detail: no user hooks (user + project settings.json hooks are {}), no dispatch-related plugins (superpowers / frontend-design / code-review only), no custom orchestration — the dispatcher behavior is stock.

Model during the session: Fable 5 (also reproduced context under Opus 4.7 pre-switch). Session ran inside tmux in a VNC X session; multiple subagents active as an in-process team.

Not attaching /bug session data (proprietary project content); happy to answer follow-ups or run diagnostics on the preserved stale-store backup.

View original on GitHub ↗