[BUG] Desktop local agent mode: spawn_task task sessions busy-loop at ~100% CPU and are never reaped (survive 44h+) — macOS 2.1.170
Preflight Checklist
- [x] I have searched existing issues and this exact variant hasn't been reported.
- [x] This is a single bug report.
- [x] I am using the latest version of Claude Code.
Summary
In Claude desktop "local agent mode" (cowork), task sessions spawned via mcp__ccd_session__spawn_task can wedge into a single-core busy-loop (~94–99% CPU each) and are never reaped. Three such children survived ~44 hours pinning a core each, long after their work stopped, until killed manually. The harness has no wall-clock or output-stall watchdog on spawned task sessions, so a wedged child runs until the user notices the fans.
This looks like the same root-cause family as #63196 (closed/completed, but reportedly still occurring through 2.1.161) — a Node busy-wait on a socket/pipe instead of blocking I/O — but on a different launch surface: #63196 is the claude agents CLI daemon / bg-spare / bg-pty-host pool; this is local-agent-mode spawn_task task sessions (full --output-format stream-json sessions with model/tools/plugins). Filing separately so the surface is captured; merge into #63196 if maintainers determine it's identical.
Environment
- Claude Code: 2.1.170 (Claude.app local agent mode / cowork)
- macOS 26.2 (Build 25C56), Darwin 25.2.0, arm64 (Apple Silicon)
Evidence
Three claude processes, each pinning a core for ~44h. Live ps at discovery:
PID %CPU ELAPSED STARTED NOTES
58346 98.5 01-21:16:22 Sun Jun 14 11:40:50 model=default, spawn_task child
35795 97.9 01-20:05:16 Sun Jun 14 12:51:56 model=default, spawn_task child
60819 99.1 01-18:20:12 Sun Jun 14 14:37:00 model=opus, --resume <uuid>
System load average reached 8.43 on a 10-core machine, almost entirely these three.
All three argv contained the local-agent-mode fingerprint:--output-format stream-json --input-format stream-json --include-partial-messages,--allowedTools ...,mcp__ccd_session__spawn_task,...,--plugin-dir .../local-agent-mode-sessions/<session-uuid>/...
They belong to one long-lived cowork session (<session-uuid>, 76 spawned task transcripts on disk).
Key signal — the transcript froze at the instant of the spin. Each task's local_<id>.json stopped being written exactly when its process started spinning:
| PID | Started | Task transcript last write |
|-----|---------|----------------------------|
| 58346 | 11:40:50 | 11:39 |
| 60819 | 14:37:00 | 14:37 (exact) |
i.e. the task produced its final stream output, then the process should have exited but instead fell into a tight loop and never terminated.
Three independent signals say userspace busy-loop, not a kernel hang or idle leak:
- Sustained ~99% of one core per process (a deadlock/I-O-wait sits near 0%).
- Died to plain SIGTERM (
kill, no-9) — interruptible userspace, notD-state. - Each held a live non-PID-1 parent — the cowork supervisor kept parenting them but never timed them out or reaped them.
The --input-format stream-json --include-partial-messages flag set plus the transcript freezing at the wedge instant points to a stuck stream read/parse loop on a half-closed pipe — consistent with #63196's "busy-wait on a socket instead of blocking I/O" hypothesis.
Expected Behavior
A spawned task session that stops making progress should be bounded: a wall-clock and/or output-stall watchdog should SIGTERM→SIGKILL the child (and its process group) rather than letting it spin indefinitely. Idle/finished task processes should consume ~0% CPU.
Steps to Reproduce
Not deterministic. Observed in a long-lived local-agent-mode (cowork) session that spawned many tasks via spawn_task over a day; occasionally a child wedges at ~100% CPU after its last stream write and never exits. Suggested investigation: the idle/teardown path of a spawned task session's stream-json reader when the far end of the pipe half-closes.
Workaround
ps -Aceo pid,pcpu,etime,comm -r | grep claude # find the long-ELAPSED ~99% offenders
kill <pid> # SIGTERM is sufficient; no -9 needed
Related
- #63196 (closed/completed; reportedly still occurring 2.1.161) — same suspected root cause (Node busy-wait on socket), different surface (
claude agentsdaemon /bg-spare). This report extends the timeline to 2.1.170 and adds thespawn_task/local-agent-mode surface. - #54626 — scheduled tasks / background sub-agents leak without cleanup; names the same gap (no per-task wall-clock timeout, hung children never SIGKILLed) but for the idle-block/memory case rather than the CPU-spin case.
- #60116 —
--resume <uuid>spawns a fresh child without terminating the prior one (overlaps the--resumeorphan above; memory rather than CPU framing).
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗