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.
6 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
This is a severe liveness failure because the process does not simply crash and restart, it remains alive while becoming operationally dead: 100% CPU on the main thread, no useful input handling, API connectivity gone, and no recovery path from inside the session.
That combination is especially damaging because it creates ambiguity for operators. A dead process is obvious. A process that still exists, still holds local connections, but silently stops making forward progress is much harder to reason about and can waste a lot of debugging time.
The repeated pattern across multiple sessions in the same project also suggests this is not just a one-off machine hiccup. Even without a minimal trigger, the report already establishes a meaningful runtime-failure class: the host surface can enter a non-recovering busy-loop state under heavy tool/plugin load.
Confirming independently on a different setup — not plugin-interaction specific.
Environment
Trigger
After ~30 tool calls, the assistant entered a final "check + synthesis" step. At freeze time the UI showed the task still running at 7m 49s with ~28.8k tokens downloaded — i.e. the model was actively streaming a very long final response.
Symptoms (same as OP)
Notable variant: simpler environment than OP (1 MCP server, no hooks, no third-party plugins), which rules out plugin/hook interactions as a necessary condition. The O(N²) rendering hypothesis on large streamed markdown fits: xhigh effort + Opus 4.7 produces notably larger final outputs, pushing the renderer past a threshold where per-token rendering cost compounds. Prior sessions with the same MCP on Opus 4.6 at default effort (smaller streams) did not trigger this.
Same symptom on Linux. Adding a data point because this is a clear regression — same workflow didn't have this problem a couple of months ago.
Environment
Process state when hung
Proof that the event loop is dead, not just busy — 3-second sample of
/proc/<pid>/status:Zero voluntary context switches means the process never yields for I/O — consistent with V8 trapped in a full-GC loop on a near-max heap, not doing actual work.
SIGTERMignored,SIGKILLrequired.Regression window: works fine on older versions, started hanging on long sessions recently — matches the timeline in #33380 / #27421 pointing at post-Sonnet-4.6 ArrayBuffer accumulation. Heavy tool outputs (large dmesg / file reads) appear to accelerate it significantly, consistent with HTTP-response-body retention.
Happy to capture a heap snapshot next time if useful — any preferred flag / NODE_OPTIONS to enable it before the process becomes unresponsive?
This seems to have happened to me on Linux (Pop!_OS). Claude Code wrote this:
Claude Code 2.1.123 — main thread spins after parallel MCP tool returns
Environment
Symptom
Session became unresponsive ~13 min after start. Process kept running at ~94% CPU on a single core for 2h+ without writing any further events to its
~/.claude/projects/<dir>/<session>.jsonl. Ctrl-C, Ctrl-D, repeated, ineffective. SIGKILL was the only way out.
Last entries on the transcript before freeze
Evidence collected from /proc while still hung
/ event loop.
non-disk fd.
Hypothesis
After two parallel mg_api (jikan stdio MCP) tool calls returned, the agent loop entered a busy-poll on a child stdio fd that was at EOF / EAGAIN, missing a readable (or analogous Bun
stream) event registration. The task_reminder attachment being inserted at the same moment as the 2nd parallel result may be relevant — possibly a race in the inbound-message merge path
when an attachment lands between two near-simultaneous tool results.
Repro hints (best guess, not verified)
Recovery
kill -9 of the main PID. Transcript on disk is complete up to the freeze point — no data loss.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.