Skip to content

Copilot CLI models start shell and background tasks but dont understand if they are finished #4385

Description

@npechett-wtg

Tasks is done I see the output file but model is waiting forever as background task is not ending by itself.

Fix this please.

Adding diagnostic evidence, since the original report was light on detail. I reproduced this and traced it to the OS level. The shell process has already exited — it is left as an unreaped zombie while the CLI continues to report it as running.

Environment

  • Copilot CLI: 1.0.75 (no fix in 1.0.76–1.0.79-5 release notes)
  • OS: RHEL 8 (Linux, x86_64)
  • Node: v24.11.1
  • Shell: bash, spawned by the CLI as bash --norc --noprofile -c ...

Reproduction

Run any trivial command through the shell tool:

date +%s.%N; echo MARKER_START; ls -d ~/.copilot >/dev/null; echo MARKER_END; date +%s.%N

Output returns immediately and completely:

1786008693.683126807
MARKER_START
MARKER_END
1786008693.689291574

The timestamps show the command ran in 6 milliseconds. The tool then reports:

command ... is still running after 30 seconds

30+ seconds later, list_bash still shows:

shellId: 9, pid: 60889, mode: sync, status: running, elapsed: 152s, commands: 1, unread output: yes

Root cause: the process is a zombie

Inspecting the PIDs the CLI itself reported as running:

for p in 61902 60889 61404; do
  if [ -d /proc/$p ]; then
    echo "PID $p state=$(awk '{print $3}' /proc/$p/stat) cmd=$(tr '\0' ' ' </proc/$p/cmdline)"
  fi
done
PID 61902 ALIVE state=Z cmd=
PID 60889 ALIVE state=Z cmd=
PID 61404 ALIVE state=Z cmd=

All three are in state Z (zombie) with an empty cmdline. The processes have terminated and their exit statuses are sitting in the process table waiting to be collected — the parent has not called waitpid(). Every command tested ends this way, so it is not command-specific.

Ruling out the obvious explanations

I tested the two usual causes of a "hanging" shell. Both are ruled out:

1. Not an orphaned child holding the stdout pipe. The child is fully dead (state Z), and explicitly closing both output streams changes nothing:

exec 3>&1; { date; echo REDIR_TEST; } >/tmp/rt.log 2>&1; exec 1>&- 2>&-; cat /tmp/rt.log >&3; exec 3>&-

→ output returns, session still reports running.

2. Not shell rc/profile config. The CLI spawns with --norc --noprofile, and ~/.bashrc / ~/.profile contain no background jobs. An explicit trailing exit 0 also does not help:

date +%s.%N; echo TEST_EXPLICIT_EXIT; exit 0

→ output returns, session still reports running.

Additionally, the CLI process holds both ends of its own pipes:

lr-x------ 10 -> pipe:[915443282]     # read end
l-wx------ 11 -> pipe:[915443282]     # write end  (same inode)

With the write end held open by the reader itself, read() on that pipe can never see EOF regardless of what the child does.

Why this matters

Because no command-side change can fix it, this cannot be worked around by users:

  • A preToolUse hook that rewrites the command (setsid + wait + kill the process group) has no effect — there is no orphan to reap and the pipe is held by the parent.
  • The only reliable mitigation is calling stop_bash after every single command, which depends on the model remembering to do so on every turn. In practice it does not, so sessions accumulate phantom "running" shells.

The downstream effect is the behavior in the issue title: the model sees status: running, concludes work is still in progress, and either waits indefinitely or re-runs the command. Long-lived processes started in earlier sessions then survive indefinitely — I found a node server orphaned 56 days ago from a prior Copilot session still running on this host.

Expected behavior

The CLI should waitpid() the shell process once it exits and mark the session complete, rather than leaving it as a zombie reported as running. Output delivery already works correctly; only the completion/reaping step is broken.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions