[BUG] 100% CPU freeze on TaskStop — runaway posix_spawn loop in Bun runtime
Bug Description
Claude Code enters an infinite CPU loop (100% single core) when a background task is stopped via TaskStop (the internal tool invoked when canceling/stopping a background Bash command). The process becomes completely unresponsive — no input accepted, no API calls, no progress. The system becomes starved for CPU: other processes cannot fork, the terminal is unusable, and the only remedy is kill -9 from a pre-existing shell.
This is 100% reproducible and has persisted across every version for the past month (at minimum v2.1.9 through v2.1.50).
Environment
| Component | Value |
|-----------|-------|
| Claude Code Version | 2.1.50 (reproduced since ~2.1.9) |
| Platform | macOS 26.2 (Darwin 25.2.0) |
| Architecture | ARM64 (Apple Silicon M4 Pro) |
| CPU | 12 cores |
| RAM | 32 GB |
| Shell | zsh |
| Terminal | tmux inside iTerm2 |
Steps to Reproduce
- Start a Claude Code session
- Ask Claude to run any Bash command with
run_in_background: true
(e.g., sleep 60, npm test, any long-running command)
- Ask Claude to stop the background task — this triggers the internal
TaskStoptool - Immediately: Claude Code process pegs 100% CPU on one core
- Terminal is unresponsive — cannot type, no output
- System becomes sluggish — other terminals lag, new process spawning fails
- Only remedy:
kill -9 <pid>from a pre-existing terminal session
The trigger is specifically TaskStop. Running background tasks alone is fine. The freeze occurs at the moment the stop/cancel is requested.
Symptoms
- 100% CPU on single core — main thread spinning, never yields to event loop
- Complete UI freeze — no response to any input
- System-wide starvation — cannot fork new processes (macOS becomes unresponsive)
- Cannot interrupt — Ctrl+C does nothing, only
kill -9works - Network connections remain open (server-side appears fine)
- Work in progress is lost — must force-kill and lose session state
Profiling Evidence (attached)
CPU Timeline captured with 1s-resolution ps polling during live reproduction:
13:02:37-43 → 12-22% CPU, state S+ (normal, processing API)
13:02:44 → 45% CPU (TaskStop invoked)
13:02:45 → 59% CPU (spike begins)
13:02:50 → 80% CPU, state R+ (runaway loop)
13:02:51-58 → 41-66% CPU, state U+ (UNINTERRUPTIBLE)
13:02:58 → "fork: Resource temporarily unavailable" ← SYSTEM STARVED
sample call-stack profiler (33,579 samples on main thread):
The main thread (100% of samples) is stuck in a tight posix_spawn loop:
Thread_29909942 com.apple.main-thread (33579 samples, 100%)
└─ 22694 samples (67.6%): spawn loop
└─ socketpair() → 423 samples
└─ fcntl() → 160 samples (setting FD flags)
└─ setsockopt() → 137 samples
└─ posix_spawn() → 614 samples (TRYING TO SPAWN PROCESSES)
└─ posix_spawn_file_actions_adddup2() → 21 samples
The Bun runtime's process spawning machinery enters an infinite retry loop — repeatedly calling socketpair → fcntl → setsockopt → posix_spawn without yielding to the event loop.
System impact confirmed: load jumped from 4.39 to 5.90 in 21 seconds, then the diagnostic script itself got "fork: Resource temporarily unavailable" — the process exhausted the system's ability to fork.
FD snapshot: Only CHR (tty) file descriptors visible on the claude process. No PIPEs. This suggests the pipes from the killed background task were cleaned up, but the spawn-loop continued anyway — possibly a retry loop that doesn't check exit conditions.
Root Cause Hypothesis
Based on the profiling evidence:
TaskStopsends a signal to kill the background child process- The Bun runtime's process manager enters a respawn/reconnect loop — attempting to re-establish communication with the dead child via
socketpair+posix_spawn - Each spawn attempt fails or completes but finds no child, so it immediately retries
- The loop has no backoff, no max-retry, no yield — it hammers syscalls at full CPU speed
- This starves the event loop, the UI thread, and eventually the entire system
This is NOT a "busy-wait on dead pipe" (no PIPEs visible). It's a runaway respawn loop in the Bun process management layer.
Diagnostic Bundle
Attached: claude-freeze-diag.tar.gz (666KB) containing:
| File | Purpose |
|------|---------|
| 01-sample.txt | KEY EVIDENCE — macOS CPU profiler call stacks (3.4MB, 33K samples) |
| 03-cpu-timeline.csv | CPU% at 1s resolution showing spike onset |
| 05-fd-pipes.txt | File descriptor snapshots (5s intervals) |
| 06-load.txt | System load showing starvation |
| 00-baseline.txt | Environment + process state before trigger |
Related Issues
| Issue | Title | Relevance |
|-------|-------|-----------|
| #23590 | Infinite CPU loop on --resume | Same symptom (main thread loop), different trigger |
| #18532 | v2.1.9 Complete Freeze - 100% CPU | Broader freeze report, 23 comments, links to 7+ related issues |
| #4580 | 100% CPU during multi-agent task JSON serialization | Event loop wedge during task operations |
| #6474 | Unresponsive hang with 120% CPU usage | Similar system starvation pattern |
Severity
High. This bug makes run_in_background + stop/cancel completely unusable. Heavy Claude Code users who rely on background tasks (CI runners, test watchers, parallel builds) hit this multiple times per day. Each occurrence requires force-killing the session, losing all in-progress work and context.
Suggested Fix Direction
In the Bun process spawning layer used by TaskStop:
- Add max retry count and exponential backoff to the process spawn loop
- After killing a child process, do not attempt to respawn/reconnect — just clean up
- Ensure the spawn loop yields to the event loop between attempts (e.g.,
await sleep(0)) - Add a circuit breaker: if spawn fails N times in M seconds, stop trying and report an error
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗