Claude Code 2.1.116 freezes with main thread in V8/JSC tight loop (100% CPU, all Anthropic API connections lost, no recovery)
Summary
Claude Code 2.1.116 on macOS 26.4.1 (arm64) hangs indefinitely with the main thread spinning in a tight JavaScript runtime loop (100% CPU on main thread, no I/O wait). The process remains alive but unresponsive; /exit and keyboard input are ignored. New Claude Code sessions spawned from the same project exhibit the same freeze repeatedly (observed 3 times in the same day).
Processes still hold localhost connections (OTLP exporter, SigNoz, obsidian-cli MCP), but lose all Anthropic API HTTPS connections. They cannot recover.
Environment
- Claude Code version:
2.1.116 - OS: macOS 26.4.1 (25E253), arm64 (Apple Silicon)
- Terminal: Ghostty (
TERM=xterm-ghostty) via Zellij multiplexer - Plugins loaded at time of freeze:
spec-workflow,persistent-logging,signoz-integration,chrome-devtools-mcp,maestro-mcp,basic-developer,marp-presentation,local-mcp,obsidian-rag,stitch, and others (111 plugin skills loaded per debug log) - MCP servers connected: signoz, chrome-devtools-mcp, maestro-mcp, spec-workflow (
@pimzino/spec-workflow-mcp 2.2.5), harness-mcp (Go binary), persistent-logging, ai-agent-mcp, obsidian-rag - OTel exporter active (OTLP gRPC →
localhost:4317→ SigNoz in Docker) - Shell hook chain: custom
harbesthook wrapping tool calls,rtk(Rust-based) hook rewriting Bash commands
Reproduction
Observed 3 times during spec-workflow QA phase execution on a single feature branch within the same day. Common conditions:
- Long-running session with many MCP tool invocations (especially
spec-workflow36-tool family andsignoz_*aggregation) - Large tool results being rendered (implementation logs, multi-KB markdown documents,
.test-logs/reads viartk cat) - Background sub-agent spawning via
Tasktool withrun_in_background: true /exitfrom a frozen session sometimes fails to terminate the process
A reliable trigger has not been isolated. Each freeze occurred within ~30–90 min of session start during heavy tool-use periods.
Evidence — process sampling
Captured with sudo /usr/bin/sample <PID> 2 -file stack-<PID>.txt while 3 frozen processes were still running. All 3 instances showed the same signature: main thread at 100% with an identical deep call chain through V8/JSC internals across every one of ~1300 samples in a 2-second window.
PID 32051 — sample header
Process: 2.1.116 [32051]
Code Type: ARM64
Platform: macOS
Target Type: live task
Date/Time: 2026-04-21 21:27:54.995 +0900
Launch Time: 2026-04-21 20:00:25.227 +0900
OS Version: macOS 26.4.1 (25E253)
Physical footprint: 553.7M
The process had been alive and unresponsive for ~87 minutes at sample time.
Main thread stack (all 3 processes — identical shape)
1291 Thread_xxx DispatchQueue_1: com.apple.main-thread (serial)
+ 1289 start (in dyld) + 6992
+ 1289 ??? (in 2.1.116) + 0x85f944
+ 1289 ??? (in 2.1.116) + 0x8639b8
+ 1289 ??? (in 2.1.116) + 0x2e7518
+ 1289 ??? (in 2.1.116) + 0xb3484c
+ 1289 ??? (in 2.1.116) + 0xba2834
+ 1289 ??? (in 2.1.116) + 0xe01104
+ 1289 ??? (in 2.1.116) + 0xba2620
+ 1289 ??? (in 2.1.116) + 0x6fe524
+ 1289 ??? (in 2.1.116) + 0x468700
+ 1289 ??? (in 2.1.116) + 0x2ac2bd8
+ 1289 ??? (in 2.1.116) + 0x2853078
+ 1289 ??? (in 2.1.116) + 0x34182b8
+ 1289 ??? (in <unknown binary>) [0x12193b8b8]
+ 1289 ??? (in <unknown binary>) [0x12193b888]
+ 1289 ??? (in 2.1.116) + 0x3b57dc
+ 1289 ??? (in 2.1.116) + 0x2e98c10
+ 1289 ??? (in 2.1.116) + 0x2db78bc
+ 1267 ??? (in 2.1.116) + 0x2db7a0c
: 1236 ??? (in 2.1.116) + 0x2c39508
| 1077 ??? (in 2.1.116) + 0x2c43548
+ 1049 ??? (in 2.1.116) + 0x2c439b4
! 1019 ??? (in 2.1.116) + 0x34182b8
: 460 ??? (in <unknown binary>) [0x1219c0dd4]
The <unknown binary> frames in high memory (0x1219xxxxx) are JIT-compiled code — classic V8/JSC signature. The deep recursion through offsets 0x2c39508, 0x2c43548, 0x2c439b4, 0x34182b8 repeating identically across all samples indicates either an infinite loop in interpreted/JIT JavaScript or a pathologically slow synchronous operation (catastrophic-regex backtracking, O(n²) synchronous parsing, or deep recursion on cyclic input).
Symbols in the 2.1.116 binary are stripped in the shipped build; only offsets from load base are available.
Child runtime threads differ per instance
Each frozen process loaded a different set of child runtimes, suggesting the loop is reached via different tool paths (but converges to the same hot loop):
| PID | Distinctive threads | Likely trigger |
|-----|--------------------|----------------|
| 32051 | JavaScriptCore libpas scavenger, Heap Helper Thread, CFThreadLoop | JSC-based MCP child |
| 43590 | Bun Pool 7–Bun Pool 11 | Bun-based MCP child (harness-mcp spawns bunx-based subprocesses) |
| 51477 | tokio-rt-worker ×6 | Rust subprocess (likely rtk hook wrapper) |
Evidence — network & FD state
Compared via lsof -p <PID> on 3 frozen PIDs vs 1 live PID:
- Frozen processes: ~24–30 unix domain sockets (MCP stdio pipes), ~5 localhost ESTABLISHED connections to
:8003/:4317/:58080(OTel / SigNoz / obsidian-cli). Zero Anthropic API HTTPS connections. - Live process: Same localhost connections plus 4 HTTPS connections to Anthropic API (
35.190.46.17:https,[2607:6bc0::10]:https,34.149.66.137:https).
The frozen processes have lost their API transport entirely and never reconnected. This is consistent with the main thread being blocked from running the reconnect logic.
Debug log state
~/.claude/debug/ contains logs but the latest symlink points to a session from 2026-04-08 — no fresh debug log for the frozen sessions because debug mode was not enabled. Without symbols and without debug-mode traces, there is no path from the offsets to source functions from the user side.
Why this matters
- Data loss risk: session transcripts can become stale because the frozen process cannot flush final state before being
SIGKILLed. - Resource leak: each frozen process occupies ~550 MB RSS. After 3 freezes, ~1.6 GB is held by zombies.
- MCP subprocess leak: frozen parents leave child MCP processes (harness-mcp, spec-workflow-mcp, signoz-mcp, etc.) orphaned or stuck waiting on unix sockets.
- Recovery cost: manual
kill, loss of in-flight context, and session restart.
Requested
- Symbolication / source mapping of offsets
0x2c39508,0x2c43548,0x34182b8,0x2db78bcin the2.1.116ARM64 macOS binary — these are the fixed-point of the loop across 3 independent freezes. - Main-thread watchdog: detect main-thread stall >30 s and emit diagnostic state (active tool call id, last API request id, pending async operations) to
~/.claude/debug/before the session becomes fully unresponsive. - Anthropic API transport auto-reconnect: currently, once all HTTPS connections to the API drop, the process enters a wedged state with no recovery path.
- Large-payload rendering audit: review whether tool-result rendering (multi-KB markdown, JSONL transcripts, implementation logs) uses a synchronous pathway that can exhibit O(n²) or catastrophic-regex behaviour on pathological input.
Attachments
Full sample(1) outputs for the 3 frozen PIDs (191–212 KB each) are available on request. Symbols are stripped but offsets and thread topologies are preserved.
---
Reported from a day where 3 Claude Code sessions froze in sequence while driving a spec-workflow feature branch through its full phase lifecycle (requirements → qa → audit). The live session that captured these samples survived only because we switched to background-agent delegation after the 2nd freeze.
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗