Claude Desktop Preview MCP (preview_start) spawns unbounded zombie dev server processes — 7,400+ node zombies, system near-crash

Resolved 💬 3 comments Opened Apr 18, 2026 by matthallett1 Closed May 26, 2026
Note on repo: This bug is in Claude Desktop's bundled Preview MCP (Claude_Preview / preview_start, preview_logs, etc.), not the Claude Code CLI itself. Filing here because there is no public Claude Desktop repo — please route as appropriate.

Summary

The Preview MCP (preview_start) does not reap its spawned dev server / child worker processes when a dev server fails to start or dies. Combined with Claude's automatic retry behavior, this produces an exponential zombie-process accumulation. In a single Claude Desktop session targeting a Next.js 16 + Turbopack project, this grew to 7,483 zombie Node processes within ~15 minutes, maxed system load average at 899 (on a ~20-core Mac Studio), ballooned swap from a 3GB baseline to 70GB allocated, and froze macOS until the processes were manually killed via Terminal with pkill.

Severity

High — caused a full UI freeze / near-system-crash on a 128GB Mac Studio. On a more memory-constrained machine this would reliably hard-crash.

What happened

Working in a Claude Desktop conversation on a Next.js 16.2.2 + Turbopack project (grapora.com). The dev server had a workspace root resolution issue on startup. Claude's session transcript showed a repeating loop:

Used preview start
Used preview logs
Server started but Turbopack is having a workspace root resolution issue...
Background task failed
Used preview logs
Server died. Let me restart and check logs.
Used preview start
...

Each preview_start retry left behind:

  • The prior next-server process (still bound / attempting bind to port 3000)
  • All of its spawned .next/dev/build/postcss.js worker children

Diagnostic snapshot (captured mid-incident):

=== Before kill ===
7483 grapora/next processes
3 process(es) on :3000

load averages: 586.79 636.68 899.14
vm.swapusage: total = 70656.00M used = 14474.62M

Two separate next-server PIDs were simultaneously listening on :3000.

Expected behavior

  1. preview_start should track the PID group it spawns (e.g. setsid / detached process group) and kill that group on restart or failure.
  2. On dev-server failure, the MCP should reap all children (Turbopack spawns a PostCSS worker per file save — these leak fast).
  3. If the target port is already bound, the MCP should either kill the prior owner or surface a clear error rather than stacking a second server on top.
  4. Retries should be hard-capped (e.g. 3 attempts) with failure surfaced to the user instead of looped.

Actual behavior

  • No PID-group tracking — orphaned children survive indefinitely
  • No port-conflict handling — stacks two next-server on :3000
  • Unbounded retries — Claude keeps calling preview_start as long as the server "dies"
  • User CLAUDE.md dev-server discipline rules are not honored by the Preview MCP

Reproduction

  1. Open Claude Desktop against any Next.js 16 + Turbopack project whose dev server fails on startup (any compile error or workspace misconfig works)
  2. Ask Claude to start the dev server and verify a page
  3. Observe: preview_start is called repeatedly; ps -ax | grep '.next/dev/build/postcss.js' | wc -l grows rapidly
  4. Within minutes: thousands of zombie Node processes; load avg spikes into the hundreds; swap balloons

Environment

  • Claude Desktop: current as of 2026-04-18
  • Claude Code CLI (embedded): 2.1.111
  • OS: macOS on Apple Silicon (Mac Studio, 128GB RAM, ~20 cores)
  • Runtime: Node 20 / Next.js 16.2.2 / Turbopack

Suggested fixes

  1. Spawn the dev server in its own process group (detached: true + process.kill(-pid) on cleanup) and kill the group before spawning a new one.
  2. Pre-flight port check — abort with a clear error if the port is occupied by a prior preview_start that wasn't reaped.
  3. Hard-cap preview_start retries per session (3 attempts, then report failure to the user instead of auto-looping).
  4. Honor a user's CLAUDE.md "dev-server discipline" rules, or expose a global setting to disable the Preview MCP entirely.

Recovery

pkill -9 -f 'next-server' + pkill -9 -f '.next/dev/build' + lsof -ti:3000 | xargs kill -9 cleared the 7,483 zombies and let macOS reclaim the 70GB of swap. Load normalized within minutes.

Additional evidence

Full ps snapshots, load/swap timeline, and the full Claude Desktop session transcript available on request.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗