[BUG] Runaway node fork storm (~43 procs/sec) from claude-code sessions exhausts 48GB RAM → 3 macOS kernel panics in 24h (forensic data included)

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 3 comments · opened Jul 22, 2026

Summary

claude-code sessions on macOS trigger runaway node process fork storms (up to 43 new processes/second), exhausting 48 GB of RAM within minutes and causing 3 macOS kernel panics (watchdog timeout) within 24 hours. I have detailed forensic data from JetsamEvent reports, disk-writes resource diagnostics, and a 10-second-interval monitoring log that captured a storm in real time.

This appears to combine two known issues — MCP/child processes never reaped on session end (#33947, #40667, #36616) and unbounded process multiplication across concurrent sessions (#45880) — but escalates them to full kernel panics, with hard numbers.

Environment

  • macOS 26.5.2 (25F84), MacBook Pro Mac17,8 (Apple Silicon T6050, 18 cores, 48 GB RAM)
  • claude-code 2.1.217 (bundled with Claude desktop 1.24012.1; also used inside cmux 0.62.2→0.64.20)
  • node v25.8.1 (homebrew)
  • ~10 stdio MCP servers configured (npx-based), later reduced as mitigation
  • Workload: agentic editing on a Next.js 16 project (subagent-driven), i.e. frequent file edits while next build/dev tooling spawns SWC workers

Incident timeline (all 2026-07, KST)

| When | Event |
|---|---|
| 07-19 08:19 | JetsamEvent — largest: Virtualization VM; Claude + node pressure |
| 07-22 06:18 | JetsamEvent + WindowServer watchdog timeout + shutdown stall |
| 07-22 08:18 | Kernel panic (watchdog) after OOM |
| 07-23 05:52 | JetsamEvent: 2,760 node processes, 111 GB combined rpages, inside a cmux-led coalition running claude-code 2.1.217. One node held 12,800 open fds (cpuTime 107 s). Free memory 261 MB at capture |
| 07-23 05:56 | Kernel panic (watchdog, wdog boot fault) |
| 07-23 07:24 | Storm captured live by my 10s watchdog log (below). Heaviest process: next-swc.darwin-arm64.node under coalition com.anthropic.claudefordesktop; single node dirtied 8.59 GB file-backed memory over 1,842 s (disk-writes diag) |
| 07-23 07:29 | Kernel panic (watchdog timeout: "no checkins from monitoring") |

Real-time capture of the storm (10s polling)

07:24:35 node=5    free=93%
07:24:55 node=867  free=63%   <- +862 processes in 20 seconds (~43/sec)
07:25:18 node=2149 free=30%   <- kill attempts could not keep up
(no further log entries possible — system unresponsive)
07:29    kernel panic, forced reboot

Notable: my cleanup script SIGKILLed matching nodes twice during the storm; respawn outpaced the kills, and once memory was exhausted the script itself could no longer be scheduled.

Evidence of the two contributing defects

  1. Orphaned children: after sessions end, npx-launched MCP node processes remain alive reparented to launchd (PPID=1). Observed repeatedly; matches #33947 / #40667.
  2. Unbounded multiplication: each claude session spawns its own full set of stdio MCP servers; concurrent sessions/subagents multiply them with no global cap or memory ceiling; matches #28860 / #45880. Combined with an agentic edit loop on a Next.js project (SWC/compile workers respawning per rebuild), spawn rate reached ~43 procs/sec.

Impact

Three full kernel panics (watchdog) in 24 h; total data-loss risk for anything unsaved; machine unusable during storms. This is not graceful degradation — macOS's Jetsam could not contain it.

Mitigations I had to apply (workarounds, not fixes)

  • kern.maxprocperuid lowered 8000 → 800 via LaunchDaemon (kernel-enforced hard stop)
  • 10-second watchdog LaunchAgent that SIGKILLs node/bun/next workers on count/rate triggers
  • Reduced MCP server count; project CLAUDE.md rules forbidding watch-mode tools in sessions

Requests

  1. Reap the entire process group (MCP servers, spawned build tools) reliably on session/subagent end — e.g. killpg on exit, PID tracking, or launching children in their own process group with cleanup.
  2. A global cap / rate limit on child process spawning per session, and a memory ceiling.
  3. Share MCP server processes across concurrent sessions (#28860) or at least de-duplicate identical stdio servers.

I can provide the full JetsamEvent .ips files, the disk-writes diag, and my monitoring logs on request.

View original on GitHub ↗

3 Comments

cuveodev · 19 days ago

Independent reproduction on macOS with a very similar fork pattern.

Environment

  • macOS 26.5.2 (25F84)
  • MacBook Pro Mac14,9, Apple Silicon, 16 GB RAM
  • Claude Code 2.1.227 at the time of the incident
  • Claude Code was launched manually in VS Code integrated terminals, not through the Claude VS Code extension UI
  • Four long-lived Claude Code sessions were open across two repositories

Jetsam evidence

The incident was captured in JetsamEvent-2026-08-11-235729.ips:

  • 943 processes named node in the Visual Studio Code coalition
  • Combined node rpages: 1,725,565, approximately 26,961 MiB on this system
  • All 943 node processes had the same Mach-O UUID, matching the Node binary used by a local stdio MCP/plugin runtime
  • Four Claude Code 2.1.227 processes were in the same coalition, using approximately 1,664 MiB combined
  • One node process used approximately 1,350 MiB and had been alive for about 763 seconds
  • Another used approximately 1,259 MiB, was only about 4 seconds old, and had 3,200 file descriptors
  • Many node processes were younger than four seconds, consistent with a rapid fork/respawn storm rather than only slow orphan accumulation

An earlier Jetsam capture on the same machine contained 1,137 node processes and approximately 29 GB attributed to the same pattern.

Trigger candidate and mitigation

context-mode was the only enabled local Node-based MCP plugin in this setup and had registered hooks for SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, and PreCompact. It had been disabled after the first incident but was later enabled again while older Claude sessions were still alive. The second storm occurred after that.

This correlation does not prove that context-mode itself is the root cause; Claude Code still needs a process-group cleanup and global spawn cap so a plugin/MCP failure cannot exhaust the whole machine.

Mitigations currently preventing recurrence:

  1. Disabled context-mode and removed its separate SessionStart cache-heal hook.
  2. Terminated all long-lived Claude sessions and restarted VS Code.
  3. Pinned terminal-launched Claude Code to a previously stable runtime.
  4. Added a LaunchAgent guard that terminates only the affected Node binary if its process count exceeds 60.
  5. Disabled the unused Claude VS Code extension to keep the terminal and extension lifecycles separate.

After applying those mitigations, a real terminal invocation completed successfully, left zero matching Node processes behind, and VS Code remained stable at 12 processes and roughly 1.7–1.8 GB RSS.

I can provide sanitized aggregate data from both Jetsam reports or run additional targeted diagnostics if maintainers need a specific signal.

cuveodev · 19 days ago

Correction/follow-up to my report above: the newest recurrence produced enough local evidence to identify a different immediate trigger.

In this incident Claude Code invoked npx next dev -p 3100 for a Next.js 16.2.12 project. Turbopack became ready, then the first page compilation spawned hundreds of Node children. The macOS Jetsam report captured 782 Node processes, 777 in the VS Code/devserver coalition. Switching the same project to next dev --webpack produced five successful requests while remaining at 4 total Node processes (~67 MB RSS).

So this particular recurrence was a Next.js/Turbopack worker-respawn storm launched from a Claude session, not evidence that the Claude binary itself forked the children. I reported the detailed reproduction to the related Next.js issue: https://github.com/vercel/next.js/issues/92978#issuecomment-5259697796

I am leaving the earlier Claude report in place because the original incidents did not preserve command lines, but this latest event should not be counted as an independent Claude-core reproduction.

sampoder · 19 days ago

This has been fixed in Turbopack - I would recommend updating to Next.js 16.3. Please see https://github.com/vercel/next.js/issues/92978#issuecomment-5110231290 and https://github.com/vercel/next.js/pull/96159.

I believe this can be closed?