Infinite CPU loop on --resume: main thread never yields to event loop
Bug Description
Claude Code v2.1.33 enters an infinite CPU loop (100% of one core) when resuming a session with --resume. The process becomes completely unresponsive — no user input is accepted, no network I/O occurs, and the conversation file stops being updated. The only remedy is kill.
Environment
- Version: 2.1.33
- Platform: macOS 26.1 (Darwin 25.1.0), ARM64 (Apple Silicon)
- Shell: zsh
Steps to Reproduce
- Start a session with tasks, subagents, and tool results (session had 3 tasks, 1 subagent, ~700KB conversation)
- Resume it with
claude --resume <session-id> - Process launches and immediately pegs CPU at 100%
- Terminal is unresponsive — cannot type, Ctrl+C doesn't work cleanly
Reproduced multiple times: killing and restarting with the same session reproduces it. Starting a fresh session (without --resume) in the same project directory works fine. Other Claude Code processes running concurrently on the same machine are responsive.
Configuration Context
- ~190 plugin skills installed (23 plugin packages from marketplace)
- 5 custom slash commands, 5 custom skills, 1 custom agent
- 3 MCP servers configured (1 Docker-based, 1 npm-based, 1 built-in chrome MCP)
- Pre/Post tool-use hooks configured
- Project has a
settings.local.jsonwith ~25 permission allow entries - Project directory contains multiple old conversation files (largest: 53MB)
Diagnosis via sample (macOS CPU profiler)
Four consecutive sample snapshots were taken over ~14 minutes. All show the same pattern:
Main thread (event loop) is 100% CPU executing JIT-compiled JavaScript and never yields to kevent64:
2499/2499 samples (100%) - main thread in JS execution
2309/2499 (92.4%) - single hot JIT function
1131/2309 (49%) - iterating over ~40+ elements in address range 0x120d42xxx
Each leaf address gets 10-45 samples = uniform iteration pattern
The same JIT function (address range 0x120d42818–0x120d43bf8) appears in all 4 samples, confirming it's a stable compiled function stuck in a loop, not transient compilation.
Timeline of degradation:
| Sample | Time | Main Thread | Network I/O | Conv File |
|--------|------|-------------|-------------|-----------|
| #1 | 22:04 | 100% JS | Active | Growing |
| #2 | 22:08 | 79% idle (GC pause) | Active | Growing |
| #3 | 22:11 | 100% JS | Active | Growing |
| #4 | 22:18 | 100% JS | Zero bytes | Stopped |
Memory footprint oscillated: 1.0G → 414M (GC) → 1.4G → 1.4G, peaking at 1.5G.
All worker threads (12× Bun Pool, Heap Helpers, JIT Worklist, HTTP Client, File Watcher) are properly idle/waiting — only the main thread is stuck.
Key observation: By sample #4, nettop shows identical byte counts across 2-second intervals — the process has stopped all network I/O while still burning 100% CPU. The conversation JSONL file also stopped being written. The process is doing no useful work.
Additional lsof / ps Data
PID 64712 99.9% CPU 1.5GB RSS state=R+ time=16:21
- 4 TCP connections to Anthropic API (ESTABLISHED but idle)
- 1 TCP to GCP, 1 to AWS (MCP servers)
- 24 unix domain sockets (MCP stdio pipes)
- 86 total file descriptors
- Child processes: context7 MCP, docker MCP, chrome MCP, pyright, typescript-language-server, gopls (all idle, 0% CPU)
Sample Files
<details>
<summary>Sample #4 (22:18) - Sort by top of stack</summary>
Sort by top of stack, same collapsed (when >= 5):
__ulock_wait2 (in libsystem_kernel.dylib) 29988
__psynch_cvwait (in libsystem_kernel.dylib) 14957
mach_msg2_trap (in libsystem_kernel.dylib) 2501
__workq_kernreturn (in libsystem_kernel.dylib) 2499
kevent (in libsystem_kernel.dylib) 2499
kevent64 (in libsystem_kernel.dylib) 2499
??? (in <unknown binary>) [0x1211abb90] 132
??? (in <unknown binary>) [0x120d42cf4] 45
DYLD-STUB$$pthread_getspecific (in 2.1.33) 34
??? (in 2.1.33) load address 0x102170000 + 0x16cdf7c 30
_platform_memmove (in libsystem_platform.dylib) 30
??? (in <unknown binary>) [0x120d42cd4] 29
??? (in <unknown binary>) [0x1211ab9e4] 26
??? (in <unknown binary>) [0x120d42cb4] 24
??? (in <unknown binary>) [0x120d43bf8] 24
</details>
<details>
<summary>Sample #4 (22:18) - Main thread call graph (top)</summary>
2499 Thread_27819082 DispatchQueue_1: com.apple.main-thread (serial)
+ 2499 start (in dyld) + 7184
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x3f90
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x536c
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x26a430
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x1121cd0
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x2ab770
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x4367bc
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x13445c0
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x20b6aec
+ 2499 ??? (in 2.1.33) load address 0x102170000 + 0x2ee8db4
+ 2499 ??? (in <unknown binary>) [JIT code]
+ ... deep JIT call chain (never yields) ...
+ 2309 ??? (in <unknown binary>) [hot iterative function]
+ 1131 ??? [0x1211ab88c] → 40+ leaf iterations in 0x120d42xxx
</details>
Expected Behavior
claude --resume should load the session and present an interactive prompt within a few seconds.
Actual Behavior
Process immediately enters 100% CPU infinite loop in JIT-compiled JavaScript. Never becomes interactive. Must be killed externally.
🤖 Generated with Claude Code
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗