Orphaned claude.exe subprocesses (resume flag) accumulate after closing Claude Desktop session windows on Windows
## Summary
Closing a Claude Code session window inside the Claude Desktop app does not terminate the underlying claude.exe --resume <uuid> subprocess. These orphans accumulate over hours/days, each holding ~50-75 MB of RAM and an open stdin pipe to the parent Electron main process.
Environment
- OS: Windows 11 Pro 10.0.26200
- Claude Desktop: 1.7196.0.0 (
C:\Program Files\WindowsApps\Claude_1.7196.0.0_x64__pzs8sxrjxfjjc\app\claude.exe) - Claude Code: 2.1.138 (
%AppData%\Roaming\Claude\claude-code\2.1.138\claude.exe) - Repro: consistent — observed 6 orphans after ~6 hours of normal use
Reproduction
- Open Claude Desktop and start a Claude Code session.
- Have a brief conversation so the session gets a UUID and is persisted.
- Close the session window (X button) — the UI is gone.
- Inspect processes:
``powershell``
Get-CimInstance Win32_Process -Filter "Name='claude.exe'" |
Where-Object { $_.CommandLine -like "*--resume*" } |
Select-Object ProcessId,
@{N='AgeMin';E={[math]::Round(((Get-Date) - $_.CreationDate).TotalMinutes,0)}},
@{N='MemMB';E={[math]::Round($_.WorkingSetSize/1MB,1)}}
- Observe: the
claude.exe --resume <uuid>process for the closed session is still running, still parented to the Claude Desktop main process. It will sit idle indefinitely.
Actual observation
8 claude.exe processes running, 6 of them orphans aged 5–6 hours with no corresponding UI window. All share parent PID matching the Claude Desktop main process and were launched with --output-format stream-json --verbose --input-format stream-json ... --resume <uuid>.
Expected behavior
When a session window closes, the corresponding claude.exe --resume subprocess should terminate within a few seconds — via explicit Stop-Process/SIGTERM from the parent on renderer destroy, closing the stdin pipe so the child reads EOF and exits, or a Windows Job Object association so children die with the parent.
Why the leak happens (best guess)
- The
claude.exe --resumesubprocess is spawned by the Electron main process, not the renderer hosting the session UI. - When a renderer is destroyed (window closed), the main process keeps running and keeps the stdin pipe to the subprocess open.
- The subprocess blocks on
read from stdinwaiting for the next stream-json message, never receives EOF, never exits. - No idle timeout or heartbeat detects the lost UI peer.
Workaround
Get-CimInstance Win32_Process -Filter "Name='claude.exe'" |
Where-Object {
$_.CommandLine -like "*--resume*" -and
((Get-Date) - $_.CreationDate).TotalHours -gt 2
} |
ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
Suggested fixes (in order of robustness)
- Windows Job Object with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEso children die if Desktop main dies. - Renderer lifecycle hook — track renderer-to-subprocess mapping; kill subprocess on renderer
closedevent. - Close stdin pipe on UI close so child reads EOF and exits cleanly.
- Idle timeout in the CLI — exit after N minutes of stdin silence when launched in stream-json mode.
Impact
- ~50-75 MB RAM leak per closed session
- Pollutes process list, making it harder to identify real active sessions
- Confusing for users — looks like Claude Code has a phantom session problem when it is actually a Desktop wrapper issue
- Could mask other subprocess leaks (MCP server connections, child agents)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗