Orphaned subagent process leaks memory when parent terminal session terminated
Status Closed — not planned
Reported on v2.1.17
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 15 comments · opened Jan 23, 2026 · closed May 28, 2026
Description
When a parent Claude Code terminal session is terminated (e.g., VS Code integrated terminal closed/killed), subagents spawned via the Task tool are not properly cleaned up. The orphaned subagent process accumulates massive memory (30GB in this case) and must be manually killed.
Steps to Reproduce
- Run Claude Code in VS Code integrated terminal
- Spawn a subagent using the Task tool (in this case,
n8n-workflowsubagent to monitor an ongoing execution) - Parent session freezes or becomes unresponsive
- Terminate the VS Code terminal
- Subagent process is orphaned (reparented to launchd/PID 1 on macOS)
Observed Behavior
- Subagent process continues running detached
- Process accumulates ~30GB memory in WebKit Malloc (mostly swapped to disk)
- Process state shows as uninterruptible wait (
U) - Regular
killdoesn't work; requireskill -9 - Briefly becomes zombie before cleanup
Memory Profile (from vmmap -summary)
Physical footprint: 30.8G
Physical footprint (peak): 30.8G
WebKit Malloc 37.0G virtual 708.9M resident 29.8G swapped
Expected Behavior
- Subagent processes should be terminated when parent session exits
- If orphaned, processes should not leak memory indefinitely
- Graceful signal handling (SIGTERM should work)
Environment
- Claude Code version: 2.1.17
- Platform: macOS 26.2 (Darwin 25.2.0, ARM64)
- Terminal: VS Code integrated terminal
- Subagent type:
n8n-workflow
Additional Context
Process details before kill:
PID PPID RSS VSZ COMMAND
73002 1 479984 538806112 claude
Parent PID 1 (launchd) confirms orphaned state.
15 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Additional Trigger: Active Session Freeze During Skill/MCP Call
Found another variant of this memory leak that affects active sessions (not orphaned subagents).
Trigger Scenario
Claude session freezes when initiating a Skill tool that calls an MCP server:
The session was invoking a Supabase MCP skill when it entered uninterruptible state.
Process State
Memory Profile (vmmap)
Same WebKit Malloc leak pattern as the orphan case.
MCP Child Processes (all healthy)
The MCP children are fine - the leak is in the parent Claude process.
Key Differences from Orphan Case
| Attribute | Issue #20369 (Orphan) | This Case (Active) |
|-----------|----------------------|-------------------|
| Process type | Spawned subagent | Main session |
| Parent | Reparented to PID 1 | Active zsh |
| Trigger | Parent termination | Skill/MCP call |
| Memory pattern | Same | Same |
Conclusion
The WebKit Malloc leak can be triggered by:
Both result in the same uninterruptible state and runaway memory consumption.
Environment: Claude Code 2.1.17, macOS 26.2 (Darwin 25.2.0, ARM64)
Same issue after close all terminal.
CPU screenshot:
<img width="948" height="24" alt="Image" src="https://github.com/user-attachments/assets/5eba92c8-30b1-440a-b33b-bc40e6cc7879" />
Memory screenshot:
<img width="948" height="109" alt="Image" src="https://github.com/user-attachments/assets/2f419830-b4ee-4119-b26f-f8493878b9f6" />
Additional data point: 392 orphaned processes over ~5 days
I experienced a more severe accumulation of this bug - 392 orphaned node subagent processes accumulated over approximately 5 days of usage with Claude Desktop.
Reproduction context
Evidence
All 392 processes had their
cwdset to the project directory and held open file handles to.claude/CLAUDE.mdand.claude/settings.local.json:Sample from
lsofoutput showing the pattern:This pattern repeated for all 392 processes. Many appeared to be observer/monitoring subagents with
--disallowedToolsflags:Workaround used
Observations
This suggests the orphaning happens whenever parent sessions end without proper cleanup, regardless of termination reason (context exhausted, user action, etc.).
Fix Verification: v2.1.19 Addresses Both Orphan and Active Session Scenarios
Following up on my earlier comment about the Skill/MCP trigger variant — I've verified that v2.1.19 appears to fix both scenarios.
Test Environment
Tests Performed
| Test | Scenario | Result |
|------|----------|--------|
| 1 | Direct MCP call (
mcp__supabase__get_logs) | ✅ Passed || 2 | Skill wrapper invoking MCP (
Notion:notion-find) | ✅ Passed |Memory Observations
| Phase | Total Claude RSS |
|-------|------------------|
| Before direct MCP | 1.80 GB |
| After direct MCP | 1.61 GB |
| Before Skill wrapper | 1.76 GB |
| After Skill wrapper | 1.66 GB |
No memory spike, no freeze, no uninterruptible state. Normal GC behavior observed.
Relevant Fix in 2.1.19
This fix appears to address the underlying issue that caused both:
Conclusion
The EIO error handling fix likely prevents the process from entering the uninterruptible state that led to the WebKit memory leak. Will continue monitoring in production usage, but initial verification is positive.
Update: v2.1.19 Fix Does NOT Address Task/Subagent Trigger
Shortly after my verification comment, encountered the same bug on v2.1.19 with a different trigger pattern.
New Trigger: Task Tool Spawning Subagents
| Detail | Value |
|--------|-------|
| Version | 2.1.19 |
| Trigger | Task tool with
n8n-workflowsubagent || Process state | U+ (uninterruptible) |
| Memory at discovery | 1.1 GB |
| Elapsed time | 6h 20m |
| Zombie children | 2 defunct processes |
Process Tree (Truncated)
Analysis
The v2.1.19 fix (EIO error handling) addresses:
But does NOT address:
The zombie children indicate the subagent processes terminated but the parent couldn't reap them because it was already in uninterruptible state. This suggests the parent enters U+ state before the children finish, possibly during:
Workaround
kill -9 <PID>successfully terminated the stuck process. Children were automatically cleaned up.Suggested Investigation
The Task tool's process management may have a different code path than direct MCP calls. Worth checking if the same EIO error handling was applied to subagent spawning/communication.
Workaround: Automated cleanup script
I hit the same issue — 407 orphaned processes, 17.8GB RAM, 49GB swap on a 32GB machine, load average 452. Built an automated workaround that's been running reliably for me.
Script: https://gist.github.com/NathanSkene/d1ba60226201dbc998f200f83be8a2c0
What it does
Finds and kills orphaned Claude Code processes (PPID=1) that match
.claude/plugins,.claude/shell-snapshots, or@anthropicin their command line. Has a 120-second age guard so it won't kill active subagents.Setup (macOS)
Full setup instructions (launchd plist, cron for Linux, Stop hook config) in the gist README.
Becomes a harmless no-op if/when this is fixed upstream.
Additional data point: Recurring orphaned subagent accumulation (macOS)
This issue has been affecting me repeatedly over the past several days. The orphaned subagents don't just leak one at a time — they accumulate across sessions, causing severe system-wide degradation.
Timeline of incidents
| Date | Orphaned Claude processes | Load average | RAM (of 32GB) |
|------|--------------------------|--------------|----------------|
| Feb 4 | 214 | 27.41 | 31GB used |
| Feb 5 | 301 | 140.59 | 31GB used, 70MB free |
| Feb 7 | 211 | 12.07 | 31GB used, 96MB free |
Key observations
--disallowedToolsin their command line--resumesession ID, suggesting failed session resumption attempts that spawn new processes without terminating previous onesSystem info
Workaround
This kills only the orphaned subagents without affecting active Claude sessions. A cron job running this periodically is a stopgap, but proper process lifecycle management (parent exit → child cleanup) is needed.
For anyone still dealing with this, I put together an automated cleanup solution that handles orphan processes at three layers:
claude-ramto check RAM breakdown,claude-cleanupfor manual cleanupThe idea is: Stop hook handles normal exits, the daemon catches crashes/force-quits, and shell aliases are there when you just want to check or clean manually.
Repo with one-command installer: https://github.com/theQuert/cc-reaper
In my case this brought usage down from 7.2 GB → 4.5 GB (with 5 sessions running). The main culprits were 13 orphan subagents (~2.6 GB) and 38 duplicate MCP server processes (~1.6 GB) that accumulated over ~12 hours.
Evidence Update / Attribution Correction (2026-03-04)
I re-checked the watchdog artifact bundle and need to correct attribution details from my earlier note.
Confirmed for the panic
What I could not confirm for this specific crash
claude/anthropicprocess entry in the panic file./Volumes/Claude,com.anthropic.claudeLaunchServices lookups), but noclaude-codeprocess entries in the critical window.Practical takeaway
This incident is clearly a watchdog timeout under severe memory pressure. Based on current artifacts, I cannot assert direct Claude Code process causality in this panic path.
Leaving this correction here for accuracy.
Built a community workaround for this: cc-reaper
Three-layer defense against orphaned subagents and MCP servers:
claude-ramfor monitoring,claude-cleanupfor manual killsKeeps RAM under control until this is fixed upstream.
Follow-up on the
claude-cleanup.shworkaround gist after using it in production for a while.A small bug turned up in the elapsed-time parser on macOS:
ps etimevalues like08and09were being fed straight into bash arithmetic, which treats them as invalid octal and throwsvalue too great for base. The practical effect was noisy stderr and partial failure in the watchdog run, even though the overall PPID=1 approach was still the right fallback for orphan cleanup.The gist has now been updated to force base-10 parsing in
etime_to_seconds().Important scope note: this only fixes the watchdog script itself. It does not change the intended scope of the workaround — it still only targets
PPID=1orphaned Claude/MCP trees, not live Claude jobs that are still attached to a VS Code terminal process.The subagent reparenting described here is in the same family as a broader Bash-tool child orphan problem. Different pid source, same lifecycle: parent terminal dies, child reparents to launchd (PPID=1), accumulates memory forever. For the Bash-tool slice (
npm run dev,next dev,vitest --watch, and similar), I've published a plugin: claude-code-shepherd.Relevant bits for the reproduction in this issue:
~/.claude/.shepherd/<id>.json, so it survives a Claude crash.SessionStarthook scans every tracked session whose Claude pid is no longer alive, SIGTERM then SIGKILLs the leftover trees, and posts a short summary. Default on. This is the closest thing to the "children should die when the terminal dies" ask, deferred to the next launch rather than instant.kill -9chases.The specific subagent in the reproduction is spawned via the Task tool, outside shepherd's tracking filter. The subagent family is cc-reaper's domain. Running both side by side covers the combined surface.
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
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.