[Bug] v2.1.25 Main Thread Busy-Wait: 75% of sessions spin at 60-70% CPU (WCHAN = -)
Claude Code v2.1.25 Main Thread Busy-Wait CPU Bug
Summary
Claude Code v2.1.25 exhibits a critical bug where the main event loop thread enters an infinite busy-wait loop with no wait channel (WCHAN = -), consuming 60-70% CPU continuously without performing productive work. The bug triggers immediately at session start in approximately 75% of sessions (3 out of 4 observed).
Environment
- OS: Ubuntu Linux 6.8.0-90-generic
- Claude Code Version: 2.1.25 (224MB Bun executable)
- Binary Location:
/home/ceo/.local/share/claude/versions/2.1.25 - Binary Hash:
696135f0eccaf7a4070168845146833fa4fc93a6191fe026a7517af4d2e14fec - System: Production server running 4 concurrent Claude Code sessions
- Working Directory:
/opt/NILAssist
Reproduction
Reproduction Rate: ~75% (3 out of 4 sessions affected immediately at startup)
Steps:
- Start multiple Claude Code sessions with
claude --resume - Observe CPU usage with
ps aux | grep claude - ~75% of sessions immediately spike to 60-70% CPU
- Bug persists indefinitely (observed for 90+ minutes)
Cannot be reliably reproduced - appears to be race condition or timing-dependent at session initialization.
Observed Behavior
Four Sessions Tested - Same Binary (v2.1.25)
PID 1623341: 0.6% CPU ✓ NORMAL (runtime: 1h 44m, CPU time: 40s)
PID 1628701: 69.6% CPU ✗ AFFECTED (runtime: 1h 28m, CPU time: 1h 1m)
PID 1652227: 60.9% CPU ✗ AFFECTED (runtime: 54m, CPU time: 33m)
PID 1667839: 42.4% CPU ✗ AFFECTED (runtime: 10m, CPU time: 4.2m)
Key Observation: Session 1667839 reached 58% CPU in only 45 seconds of runtime, proving the bug triggers at startup, not gradually.
Root Cause Analysis
Thread-Level Diagnosis
Normal Session (PID 1623341) - Main Thread:
LWP 1623341: 0.3% CPU, WCHAN = ep_poll ✓ HEALTHY (sleeping on epoll)
Broken Session (PID 1628701) - Main Thread:
LWP 1628701: 62.6% CPU, WCHAN = - ✗ BUSY-WAIT (no wait channel!)
Process State Comparison
Normal Session:
- STAT:
Sl+(S = sleeping, interruptible) - WCHAN:
ep_poll(blocked on epoll_wait syscall) - Context Switches: 69,952 total (42,754 voluntary / 27,198 nonvoluntary)
- Threads: 11
Broken Session:
- STAT:
Rl+(R = running/runnable - NOT sleeping!) - WCHAN:
-(no wait channel - never blocks!) - Context Switches: 1,370,914 total (1,151,794 voluntary / 219,120 nonvoluntary)
- 19.6x more context switches than normal
- Threads: 13 (includes extra
JITWorkerthread not present in normal session)
Secondary Effects
HeapHelper Threads Also Burning CPU:
HeapHelper 1: 2.0% CPU
HeapHelper 2: 1.9% CPU
HeapHelper 3: 1.7% CPU
Total: 5.6% additional CPU
Suggests the busy-wait loop is constantly allocating objects, triggering continuous garbage collection.
Syscall Analysis (Inferred)
The main thread with WCHAN = - is not calling any blocking syscall. Based on issue #10493, the likely culprit is:
epoll_pwait(epfd, events, maxevents, timeout=0, sigmask)
With timeout=0, the syscall returns immediately instead of blocking, creating a tight polling loop:
while (true) {
epoll_pwait(..., timeout=0); // Returns immediately
// Loop continues without sleeping
}
What We Tested
❌ Killed Playwright MCP Servers - NO EFFECT
Killed all Playwright MCP processes (4 node processes) for session 1652227:
- Before: 60.9% CPU
- After (2s): 60.9% CPU
- After (5s): 60.9% CPU
- After (10s): 60.9% CPU
Conclusion: MCP servers are NOT the cause. Bug is in Claude Code binary's event loop.
✓ Confirmed: Identical MCP Configuration
Both normal and broken sessions have identical MCP servers attached:
- PayPal MCP (
@paypal/mcp) - Codex MCP (
node /usr/bin/codex mcp-server) - AWS MCP (
awslabs.aws-api-mcp-server) - Playwright MCP (
@playwright/mcp)
MCP configuration is NOT a factor.
Related Issues
- #10493 - Documents similar
epoll_pwait(timeout=0)busy-wait behavior in Claude 2.0.28
Impact
Severity: HIGH
- 75% of sessions affected immediately at startup
- 60-70% CPU waste per affected session
- No workaround available - bug is in compiled binary
- Indefinite duration - does not self-resolve
- Production systems impacted - multiple concurrent sessions multiply CPU waste
- 4 concurrent sessions = 200%+ CPU waste (should be ~2-3% total)
Workarounds
None available.
- Cannot disable at runtime
- Killing MCP servers has no effect
- Downgrading to v2.1.20 unlikely to help (bug exists across versions per #10493)
- Only solution: Kill affected sessions and restart (gambling on 25% chance of healthy session)
Requested Fix
- Immediate: Identify why some sessions enter busy-wait while others don't (race condition at startup?)
- Root cause: Fix event loop to use blocking
epoll_wait()with proper timeout (not zero) - Long-term: Add CPU usage monitoring/alerting to detect busy-wait loops in production
- Release: Patch v2.1.26 with fix
Questions for Maintainers
- Why does session 1623341 sleep normally on
ep_pollwhile others spin with WCHAN =-? - What changed between session startup that causes 75% to enter busy-wait?
- Is the
JITWorkerthread (present in broken sessions, absent in normal) related? - Can you add runtime detection of
timeout=0in epoll calls to log when this occurs?
Diagnostic Commands Used
# CPU usage
ps aux | grep 'claude --resume'
# Thread-level analysis
ps -L -p <PID> -o pid,lwp,nlwp,%cpu,wchan:20,comm
# Process state
ps -p <PID> -o pid,ppid,nice,pri,psr,stat,wchan:20
# Context switches
cat /proc/<PID>/status | grep -E '^(Threads|voluntary|nonvoluntary)'
# MCP servers
pgrep -P <PID>
Reporter Information
- User: Michael Jabara (ceo)
- Date: 2026-01-30
- Investigation Duration: 90+ minutes across 4 sessions
- Analysis Assistance: Codex MCP session 019c0df5-6ba4-7c92-ad8e-9b3bcf943048
---
This bug renders Claude Code unusable in multi-session production environments. Urgent fix required.
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗