[BUG] Task() tool has no timeout — subagent hang leaves orchestrator stuck indefinitely on Windows (no MCP servers required) (refs #28126)
Summary
Task() tool blocks the orchestrator indefinitely when a subagent hits a communication failure. No timeout, no watchdog, no heartbeat. Observed: 30+ min hang on Windows while the subagent's work-product sat complete on disk and 35k tokens burned in the meantime.
Refs #28126 (closed + locked — lock message says to file new issue and link). That issue focused on MCP server duplication as the deadlock cause. This case has zero user-configured MCP servers — which strengthens the case that the parent↔subagent stdio fragility is not MCP-specific, and the missing-timeout is a real gap independent of MCP load.
Concrete incident (2026-04-16, Windows 11)
Setup:
- Claude Code (current stable, v2.1.5x)
- Windows 11 (26200)
~/.claude/settings.jsonhas nomcpServersblock- No
.mcp.jsonanywhere in the user's config or project mcp-needs-auth-cache.jsonis empty- Running a GSD workflow (
/gsd-plan-phase) which spawns gsd-planner via Task() to write 5 PLAN.md files
Timeline:
- 14:24 Task() spawned gsd-planner subagent
- 14:29
~/.claude/tasks/{uuid}/2.jsonflipped gsd-planner toin_progress - 14:32–14:52 All 5 PLAN.md files written to disk by the subagent. Last file is 28 KB, fully formed (frontmatter, tasks, threat_model, verification sections all intact).
- 14:52 Last filesystem write from the subagent.
- 14:52 → ~15:22 30 min of dead silence. Orchestrator stayed inside the Task() call. Tokens continued to burn (~35k during the hang). User force-killed the terminal.
Post-mortem evidence (task state dir persists because no cleanup fires on force-kill):
~/.claude/tasks/{uuid}/
1.json pattern-mapper status: completed
2.json gsd-planner status: in_progress (mtime 14:29 — never flipped)
3.json plan-checker status: pending (never spawned)
4.json state-update status: pending
.lock still present
Plans written by the subagent:
36-01-PLAN.md 14:32 20 KB
36-02-PLAN.md 14:36 33 KB
36-03-PLAN.md 14:42 35 KB
36-04-PLAN.md 14:46 37 KB
36-05-PLAN.md 14:52 28 KB ← last write, ends mid-conversation with parent
Every file is syntactically complete. The subagent finished its work. What didn't reach the parent was the tool-call return value.
What this rules out
- MCP duplication is not required. This system has no MCP servers configured. Whatever is hanging the parent↔subagent channel operates with zero MCP load.
- Subagent crash before completing work is not the cause. All output files are intact and complete.
- Network / external services are not involved. The subagent wrote local files only.
Root cause (hypothesis)
Something in the parent↔subagent stdio / IPC path on Windows can enter a state where:
- The subagent has done its work and emitted its final response
- The parent CLI process never reads / never reaps that response
- Task() tool call in the orchestrator blocks forever waiting
Possibilities include: pipe buffer deadlock, Windows-specific handle inheritance, JSON chunk that straddles a buffer boundary and is never flushed, subagent process exited but parent didn't get SIGCHLD-equivalent. Without runtime tracing on the CLI I can't narrow further — but the symptom is reproducible enough to warrant a runtime-level mitigation regardless of the exact cause.
Impact
Every Claude Code user running an orchestration workflow with long-running Task() calls on Windows is exposed. The failure is:
- Silent — no error surfaces to the orchestrator
- Expensive — the agent loop continues generating, burning tokens during the hang
- Unrecoverable from within the session — only terminal-kill works
- Invisible in logs — task state JSON shows "in_progress" forever
Ask
Expose a timeout option on Task() — e.g. Task(..., timeout_ms=N) or a per-session default — that:
- returns a structured "subagent timed out" error to the orchestrator when exceeded (so the orchestrator code can handle it)
- SIGTERM/SIGKILLs the subagent process tree
- removes the
~/.claude/tasks/{uuid}/dir on timeout - includes, in the error, last-stdout-timestamp + whether any files were created during the attempt (so orchestrators can recover useful work)
Even a default-off opt-in would unblock self-protection. Orchestrators could wrap Task() in a retry/fallback path; today they cannot.
Per-subagent MCP scoping requests (#4380, #4476) address one cause of hangs. This issue addresses the orchestrator's inability to detect the symptom — which matters even when MCP is not the cause, as proven here.
Workarounds (for others hitting this today)
- Split long subagent work into multiple short Task() calls. Between calls, the orchestrator is alive and can verify filesystem state, commit incrementally, and detect "files written but no completion marker."
- After a hang + terminal kill:
rm -rf ~/.claude/tasks/*and manually clean orphan node processes (Windows doesn't reap children when the parent is force-killed).
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗