Describe the bug
finished subprocesses accumulate as zombies ( state=Z ) parented to the copilot process
every session leaks independently (~2/min)
──────────────────────────
copilot PID │ age │ zombies it owns
──────────────────────────
3111579 │ 21 min │ 8
─────────────────────────
3017091 │ 1h13m │ 79
──────────────────────────
3025749 │ 1h09m │ 135
──────────────────────────
Impact:
- Misleading "still running" status; every tool call needs a manual stop.
- Slow, unreliable command completion.
- Unbounded PID growth over long/multi-session use (pid_max headroom saves most hosts —
here pid_max=4194304 with only ~2300 PIDs used — but a long day of many sessions could
approach limits).
Affected version
copilot 1.0.71
Steps to reproduce the behavior
Steps to reproduce:
- Start
copilot and run any prompt that triggers tool calls spawning subprocesses
(git status, echo, python, etc. — even a trivial echo hi; true).
- Let the command run. Its stdout prints normally and the process exits (exit 0).
- Observe in another terminal:
ps -u -o pid,ppid,stat,cmd | awk '$3 ~ /Z/'
Finished children show as <defunct> (state Z), parented directly to the copilot PID.
- The CLI also reports each finished command as "still running after N seconds"
indefinitely, requiring a manual stop.
Expected behavior
Observed behavior:
- Subprocesses become zombies and are never reaped for the life of the session.
- Each session leaks independently (~2 zombies/min under active use). Three concurrent
sessions on one host held 8, 79, and 135 zombies (~222 total).
- Verified via /proc: state=Z, wchan=0 — i.e. dead-and-unreaped, NOT blocked on I/O.
- Only cleared by exiting the owning copilot process (zombies can't be killed).
Not NFS/fsync latency (objection preempted):
- Affected children are in state
Z (already returned from exit(), wchan=0), so all
teardown incl. any fsync/stat is already complete — a process cannot be "mid-fsync"
and be a zombie.
- They persist for the entire session, not ~30s (oldest observed: 86 min, etime 01:26:38).
- A child running only the shell builtin
true (zero file I/O — nothing to fsync/stat)
still zombies; meanwhile a properly-wait()ing parent reaps an identical bash -c true
child in <1s on the same NFS filesystem.
- The "~30s" some observers report is the tool's own initial_wait poll window, after
which it prints "still running" — not the shell taking 30s to exit.
Expected behavior:
- After a spawned subprocess exits, copilot should wait()/reap it so no zombie remains.
- The corresponding tool call should be marked completed promptly once the child exits,
rather than reported as "still running" indefinitely.
Additional context
Environment:
- copilot 1.0.71 (latest on npm as of 2026-07-17)
- Node v24.16.0 (nvm)
- RHEL 8.10, kernel 4.18.0-553.141.1.el8_10.x86_64, x86_64
- copilot binary: ~/apps/bin/copilot; launched from an interactive bash (no systemd scope)
- Home dir on NFS (weka/home) — see "Not NFS" note below; ruled out as the cause
Describe the bug
finished subprocesses accumulate as zombies ( state=Z ) parented to the copilot process
every session leaks independently (~2/min)
──────────────────────────
copilot PID │ age │ zombies it owns
──────────────────────────
3111579 │ 21 min │ 8
─────────────────────────
3017091 │ 1h13m │ 79
──────────────────────────
3025749 │ 1h09m │ 135
──────────────────────────
Impact:
here pid_max=4194304 with only ~2300 PIDs used — but a long day of many sessions could
approach limits).
Affected version
copilot 1.0.71
Steps to reproduce the behavior
Steps to reproduce:
copilotand run any prompt that triggers tool calls spawning subprocesses(git status, echo, python, etc. — even a trivial
echo hi; true).ps -u -o pid,ppid,stat,cmd | awk '$3 ~ /Z/'
Finished children show as
<defunct>(state Z), parented directly to the copilot PID.indefinitely, requiring a manual stop.
Expected behavior
Observed behavior:
sessions on one host held 8, 79, and 135 zombies (~222 total).
Not NFS/fsync latency (objection preempted):
Z(already returned from exit(), wchan=0), so allteardown incl. any fsync/stat is already complete — a process cannot be "mid-fsync"
and be a zombie.
true(zero file I/O — nothing to fsync/stat)still zombies; meanwhile a properly-wait()ing parent reaps an identical
bash -c truechild in <1s on the same NFS filesystem.
which it prints "still running" — not the shell taking 30s to exit.
Expected behavior:
rather than reported as "still running" indefinitely.
Additional context
Environment: