Remote control: server leaks a ccd-cli process per resumed session on every reconnect (sessions never torn down on disconnect)

Status Open
Reported on v2.1.247
Maintainer reply ✓ Yes — amorriscode
Activity 3 comments · opened Aug 31, 2026
💡 Likely answer: A maintainer (amorriscode, contributor) responded on this thread — see the highlighted reply below.

Summary

The remote-control server (~/.claude/remote/srv/<hash>/server --serve) re-resumes its whole working set of sessions — one ccd-cli process per session — on every client reconnect, then closes the link on its own 5-minute idle timeout without tearing those session processes down. Every reconnect therefore leaks a full set of session processes. On an always-on Mac used as a remote host, this accumulated until the machine was effectively unusable.

Environment

  • macOS (Apple Silicon Mac mini used as an always-on remote host)
  • ccd-cli 2.1.247, sessions show "entrypoint": "claude-desktop"
  • Clients connecting: Claude Desktop (macOS), iOS app, claude.ai

What we measured

After a weekend of ordinary on-and-off use (~30 connect/disconnect cycles in the server log, ~10 recent unarchived conversations in the working set):

  • 148 live remote/ccd-cli processes, including 11–14 simultaneous copies of the SAME --resume=<session-id>
  • 5.3 GB RSS + 7 GB swap, load average 29 on an otherwise idle machine
  • 5 orphaned server --serve daemons dating back days (PPID 1, nothing supervises or reaps them; an abandoned daemon re-leaks on any later connect)
  • 229 leaked dirs under ~/.claude/remote/plugins/ (919 MB — one per spawn, never cleaned) and multi-MB remote-server.log.old files

Reproduction / diagnosis path

  1. Use a machine as a remote-control host with several recent (unarchived) conversations.
  2. Connect from Claude Desktop, go idle. The log shows the server closing the link itself: closing connection idle 5m2s in both directions.
  3. Reconnect. ps now shows a second ccd-cli ... --resume=<same-id> process per conversation; the first set is still running.
  4. Repeat. grep "\[Server\]" ~/.claude/remote/run/*/remote-server.log shows the connect → idle-close → reconnect cycle; the strings kill/cleanup/shutdown/terminate/reap appear zero times in 2.6 MB of log.

The working set that gets re-resumed is the user's recent unarchived conversations (we confirmed it is not pinning — no pins existed), so ordinary usage keeps refilling it; archiving conversations only shrinks the multiplier, it doesn't stop the leak.

Expected

  • On connection close (including the server's own idle timeout), the server should tear down the session processes it resumed for that connection, or at minimum reap duplicates when re-resuming the same session id.
  • Orphaned --serve daemons with no clients should eventually exit.
  • ~/.claude/remote/plugins/ spawn dirs and rotated logs should be cleaned up.

Secondary effect worth knowing

The overload this caused pushed unrelated headless claude -p automation on the same host past its wall-clock timeouts, which is how we noticed — so the blast radius extends beyond the remote-control feature itself.

We're currently containing this with a cron that kills duplicate --resume= processes (keeping the newest per session id), which works, but the fix belongs in the remote server's disconnect path.

View original on GitHub ↗

3 Comments

amorriscode contributor · 1 month ago

Fixing this, thanks for the report.

oncoursemarketing · 20 days ago

Still reproducing on ccd-cli 2.1.280 (2026-09-24), on a Linux remote host (Ubuntu 24.04, 8 cores / 16 GB) that Claude Desktop reaches over SSH. That's after the 09-08 "fixing this" comment, so either the fix hasn't shipped to this channel yet or it doesn't cover this path.

What happened

The host was under heavy load and the desktop connection dropped. On reconnect, remote-server.log shows the server re-attaching one session's existing CLI but spawning a fresh CLI for a different session while the original kept running:

19:08:21 [process.Manager] Process c815cd45-… started, PID=1232139, command=~/.claude/remote/ccd-cli/2.1.280
...
20:10:11 [Server] New connection from: @
20:10:15 [Server] closing connection @: process b84a573a-… re-attached from another connection
20:10:24 [process.Manager] Process 41a7a1da-… started, PID=1519573, command=~/.claude/remote/ccd-cli/2.1.280
...
20:18:57 [process.Manager] Process c815cd45-… exited with code -1, terminated by SIGKILL      <- killed by hand
20:19:36 [process.Manager] Process 41a7a1da-… exited with code 143, signalled at client request

PIDs 1232139 and 1519573 were running the same session (same transcript) at the same time for about 8 minutes. The orphaned copy was mid-turn and kept launching subagents and tool calls with nobody watching, and both processes appended to the same transcript .jsonl. It stopped only when we killed it manually. Because the orphan kept doing work, it added load to a host that was already overloaded, so the problem fed itself.

So in this version the server clearly can re-attach, but for some sessions on some reconnects it starts a new process instead and never reaps the old one.

A note for anyone copying the cron workaround above

In 2.1.280 the ccd-cli argv carries no --resume=<id> (or any session flag); the session arrives over stdin. Duplicates can only be identified from the environment:

  • CLAUDE_CODE_HOST_SESSION_ID=local_<uuid> in the ccd-cli's own /proc/<pid>/environ (the desktop session id, stable across the respawn)
  • CLAUDE_CODE_SESSION_ID=<transcript id> in the environ of its children (MCP servers, tool shells)

A cron keyed on --resume= finds nothing on this version. Our current workaround is a SessionStart hook: when a session (re)starts, it terminates the process tree of any other ccd-cli whose environ carries the same CLAUDE_CODE_HOST_SESSION_ID (skipping its own ancestors and descendants, so /clear and compaction are safe). It works, but as the original report says, the fix belongs in the server's reconnect path: when it can't re-attach to an existing process for a session, it should terminate that process before spawning a replacement.

Expected

On reconnect, either re-attach to the running process for every session, or terminate the old process before spawning a new one for the same session. There should never be two live processes writing one transcript.

reuvenkarasik · 14 days ago

Another data point from a Linux remote host (Ubuntu 24.04, Claude Desktop over SSH). Still reproducing through ccd-cli 2.1.284.

How many piled up in one server run (~6 days, counted from remote-server.log):

  • 112 ccd-cli processes started and 36 exited, every one with code 143, signalled at client request. 76 were still alive when the host went down.
  • The 36 that were stopped had lifetimes anywhere from a few seconds to 40h, so nothing on a timer is reaping them.
  • At roughly 230–370 MB RSS each, the 76 leftovers came to about 17 GB on their own. On a 16 GB VM with swap, the host thrashed until sshd couldn't finish a handshake and we had to hard-reset it. This happened twice in one week.

Processes from older builds aren't reaped either. The 76 still alive at the end, by version:

| ccd-cli | alive |
|---|---|
| 2.1.280 | 26 |
| 2.1.281 | 30 |
| 2.1.284 | 20 |

After the CLI updated, sessions got new 2.1.284 processes, and 56 processes from the two earlier builds kept running next to them.

For anyone adapting the workaround above: on 2.1.284 the ccd-cli argv includes --resume=<session-uuid> again, so a cleanup script may need to handle both the argv form and the CLAUDE_CODE_HOST_SESSION_ID environ form.

@amorriscode is a fix for this still planned, and is there a version we should expect it in? We're happy to collect more logs if that would help.