CopilotClient.stop() leaks the CLI server's child process tree on Windows (orphaned node/copilot.exe per session)
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 52/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- python
- Ambito
- operating-systems, tooling
Direzione di ricerca
Ispeziona client.py, in particolare CopilotClient.stop() e la creazione del processo di StdioRuntimeConnection, quindi riproduci il ciclo Windows create_session()/stop() dell’issue tracciando i processi discendenti. Confronta il comportamento del ciclo di vita tra Windows e POSIX e considera gli approcci indicati basati su Job Object o process-group. Il lavoro è completo quando l’arresto di un client avviato non lascia processi discendenti node.exe o copilot.exe orfani.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
On Windows, CopilotClient.stop() does not terminate the CLI server's child process tree — only the top-level launcher process. Every create_session() / stop() cycle therefore orphans a full copilot process tree (node.exe + the copilot.exe broker/worker/webview helpers) that survives until its own idle timeout. In a long-running app that creates one client/session per unit of work (e.g. a batch/eval harness), this is a deterministic ~1 leaked process tree per session, which accumulates and eventually OOMs the host.
Environment
github-copilot-sdk(Python)1.0.0b7(also visible by inspection in currentclient.py)- Copilot CLI runtime
1.0.66-0 - Windows 11, Python 3.11 (miniconda)
- Transport:
StdioRuntimeConnection,use_logged_in_user=True
Root cause
CopilotClient.stop() only calls terminate() / kill() on its own launcher Popen (self._process):
# client.py (stop)
if self._process and not self._is_external_server:
self._process.terminate()
try:
self._process.wait(timeout=5)
except subprocess.TimeoutExpired:
self._process.kill()
self._process = None
On Windows, terminating the launcher does not cascade to its descendants. The launcher (copilot.cmd → cmd.exe) spawns node.exe, which spawns the real copilot.exe server + helper processes; these are left orphaned and alive after stop().
Reproduction
import asyncio, psutil
from copilot import CopilotClient, StdioRuntimeConnection
CLI = r"C:\Users\<you>\AppData\Roaming\npm\copilot.cmd"
def n_copilot():
return sum(p.info["name"] == "copilot.exe"
for p in psutil.process_iter(["name"]))
async def main():
for i in range(5):
client = CopilotClient(
connection=StdioRuntimeConnection(CLI, []),
use_logged_in_user=True,
)
session = await client.create_session()
await session.send("hello")
# ...consume events until SessionIdle...
await session.disconnect()
await client.stop()
print(f"iter {i}: copilot.exe alive = {n_copilot()}")
asyncio.run(main())
Observed (Windows): copilot.exe alive grows 1, 2, 3, 4, 5 — one orphaned tree per iteration, none reaped by stop().
Expected
After await client.stop() (for a client that spawned the server), the entire CLI server process tree should be terminated, leaving no orphaned node.exe / copilot.exe processes.
Workaround
Capture the launcher PID after create_session() and kill the whole tree explicitly on teardown — e.g. psutil.Process(pid).children(recursive=True) + kill all (enumerate before killing the parent), or taskkill /F /T /PID on Windows / os.killpg on POSIX.
Suggested fix
Have the SDK own the process-tree lifecycle so stop() reaps descendants:
- Windows: assign the launcher to a Job Object with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, so the OS atomically kills the tree when the job/handle closes. - POSIX: spawn with
start_new_session=Trueandos.killpg(os.getpgid(pid), SIGKILL)on stop. - Or a
psutil-based recursive child kill insidestop().
This is likely related to the CLI-side reports of orphaned processes (e.g. github/copilot-cli#1368, #2279) but is reproducible purely through the SDK's own create_session()/stop() lifecycle.
- Lingua principale
- Java
- Stelle
- 10.5k
- Fork
- 1.5k
- Merge medio
- 1g 9h
- PR unite (30g)
- 129
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di github/copilot-sdk
-
agentic-workflows
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
github/copilot-sdk#2760 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
github/copilot-sdk#2759 ·
-
documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
github/copilot-sdk#2758 ·
-
agentic-workflows
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
github/copilot-sdk#2709 · 1 commento ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 78/100
github/copilot-sdk#2673 ·
Tutte le issue di github/copilot-sdk
Issue simili
-
area-deployment area-integrations triage:bot-seen
Difficoltà 2/5 Mezza giornata Idoneità per principianti 86/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
apache/flink-agents#1156 ·
-
[source-shopify] FAILED bulk operation without partialDataUrl is silently treated as successful Apertaarea/connectors autoteam community connectors/source/shopify needs-triage team/use type/bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100