Repository navigation
Description
Bug: wmux 0.41.0 — runaway process count / crash-loop leaves orphaned MCP child processes, huge CPU/RAM spike
Version: 0.41.0 (Windows, downgraded back to 0.38.0 as workaround)
OS: Windows 10 Pro 10.0.19045
Symptom
Shortly after updating to 0.41.0, Task Manager showed the wmux process group consuming 30.9% CPU and ~3.3 GB RAM across 251 grouped processes. Expanding the group showed it was almost entirely made of individual node.exe ("Node.js JavaScript Runtime") child processes, each relatively small (24–83 MB), but summing up massively due to sheer count. Windows Defender also flagged the sudden burst of process activity.
Screenshot of the Task Manager view (collapsed + expanded groups) available on request.
Likely root cause
Each wmux pane that runs a claude (Claude Code CLI) session spawns its own independent set of MCP-server node.exe child processes (in my setup: 6 MCP servers per session — amocrm-mcp, mpstats-mcp, imap-email-mcp ×2, mcp-google-sheets, google-tasks-mcp-server). These are not shared/reused across panes or sessions.
Between 12:38 and 13:10 (local time) on the day of the incident, 6 separate wmux crash dumps were written to %LOCALAPPDATA%\CrashDumps:
wmux.exe.3700.dmp 12:38
wmux.exe.32120.dmp 12:48
wmux.exe.22636.dmp 12:52
wmux.exe.30344.dmp 13:00
wmux.exe.18400.dmp 13:01
wmux.exe.11612.dmp 13:10
This strongly suggests wmux 0.41.0 was crash-looping — repeatedly crashing and auto-restarting (or restoring the previous session) — and on each restart it re-spawned a fresh batch of panes/CLI sessions (each pulling in its own set of MCP node child processes) without cleaning up the orphaned processes from the previous crashed instance. Over ~30 minutes this accumulated into 251 leftover node.exe processes.
Steps to reproduce (best guess, not fully confirmed)
- Update wmux to 0.41.0 with an existing multi-pane session that runs Claude Code (with several MCP servers configured in
~/.claude/.mcp.json). - Restart / let wmux restore the session.
- If wmux crashes and auto-restarts (see crash dumps), observe
node.exeprocess count climbing with each crash cycle instead of being cleaned up.
Expected behavior
- wmux (or the terminal/pane host) should terminate child processes (including MCP server subprocesses spawned by CLI tools running in panes) when a pane/session is torn down or when wmux crashes and restarts.
- A crash-loop should not be able to unboundedly multiply subprocess count.
Workaround
Rolled back to 0.38.0 via the cached installer left by the updater, which does not reproduce the issue.
Attachments available on request
- Task Manager screenshot (collapsed + expanded process group)
- Crash dump files (
%LOCALAPPDATA%\CrashDumps\wmux.exe.*.dmp, ~5.3–5.8 MB each)
Activity
amirlehmam commentedon Aug 2, 2026
Reproduced in the code and fixed — #140.
Your diagnosis was right, including the part that matters most: the problem isn't that a crash strands processes, it's that a crash-loop multiplies them.
What was happening
killAll()— the only thing that tears down pane PTYs — is reachable from exactly one place,app.on('will-quit'). That doesn't run when the process dies abnormally. And Windows doesn't tear down a process tree when its root dies; descendants are reparented and keep running.So a crash left the whole tree resident: pane shell →
claude→ its six MCP servers. Nothing at the next launch looked for them, and session restore then spawned a fresh set beside the old one. Six crash cycles is exactly how you get to 251.The tree-kill itself was never missing.
PtyManager.kill()has runtaskkill /PID <pid> /T /Fsince #65, added precisely because Claude Code's backend orphans otherwise. It just required wmux to be alive and cooperating to reach it — which is the one thing a crash guarantees it isn't.The fix
A ledger of spawned PIDs under
%APPDATA%\wmux. Each spawn appends, each kill removes,killAll()clears. The next launch claims the file and tree-kills whatever a dead owner left behind.The delicate half isn't reaping, it's not reaping the wrong thing. That list feeds
taskkill /T /Fand Windows recycles PIDs, so a stale entry could take out an unrelated process and its children. A recorded PID being alive is not evidence that it's ours. Before killing anything, each PID is re-queried throughWin32_Processand must still match the image name and the creation time recorded at spawn. Every failure path — no PowerShell, a query error, unparseable output — reaps nothing. Leaking a process is recoverable; killing your editor is not.Verified against real process trees, including that the grandchild dies (that's the MCP-server case) and that an entry whose image no longer matches is left entirely alone.
A second, smaller thing
Chasing your
node.execount turned up something else: every wmux hook process — Claude Code fires one per tool call, in every pane — stayed resident a full second because a stdin fallback timer was never cleared. Measured 1049 ms for work that finished in ~90 ms. Also fixed, 10× down.To be clear about attribution: that is not what produced your 251. The orphaning was. But it's a standing population of idle
node.exeunder multi-pane agent use, and it made your Task Manager view noisier than it had to be.What this does not fix
The crash itself. Six
.dmpfiles means a native crash, and I can't diagnose one from the outside.So yes please — I'd like the dumps. Two would be plenty (
wmux.exe.3700.dmpand any later one, so I can see whether they're the same stack). The Task Manager screenshot too, if you still have it. Attach them here, or say the word if you'd rather not post them publicly and we'll sort out another route.Until then this change makes a crash survivable rather than cumulative — it does not stop it happening. Staying on 0.38.0 is a sensible call until we know what the crash actually is, and it would genuinely help to know whether 0.38.0 has been stable for you since, or just quieter.
fix(pty): reap process trees orphaned by an abnormal exit (#139) (#140)
amirlehmam commentedon Aug 3, 2026
Fixed in #140, shipping in 0.42.0.
The gap was that
will-quitwas the only teardown path — a crash, ataskkill, or an abnormal exit never reached it, so whole process trees(
node.exeMCP children under a Claude Code PTY) survived with no parent leftto reap them. #140 adds a PTY ledger that persists live process trees, so a
fresh start reaps whatever the previous run orphaned rather than accumulating
them across crash-loops.
Note that #141 turned out to be a separate cause of main-process CPU load
in the same version — unthrottled
git.exespawning from the diff surface. Ifyou were seeing a CPU spike as well as the RAM growth, it's worth confirming
against 0.42.0 which of the two you were hitting; the reporter there was
correct that the process trees are unrelated (
git.exevsnode.exe).merge(upstream): bring amirlehmam/wmux master (0.46.0) into oh-my-pi