github/copilot-sdk · error
timed out waiting for CLI process to exit after kill
Error message
timed out waiting for CLI process to exit after kill
What it means
After sending the kill signal, the library waits up to processExitTimeout for the process to actually exit. If the process ignores SIGKILL aftermath (or is stuck in an uninterruptible kernel state) and does not exit in time, this error is joined with any kill error. It indicates a hung child process that may need manual cleanup.
Solutions
- Find and manually kill the leftover process (ps / pgrep for the CLI binary, then kill -9).
- Check for orphaned child processes holding pipes open; kill the whole process group.
- If in a container, inspect dmesg for D-state tasks and restart the container if stuck.
- Report a persistent reproducible hang to the library maintainers with the CLI version.
Defensive patterns
Strategy: fallback
Try / catch
if err := client.Stop(ctx); err != nil {
if strings.Contains(err.Error(), "timed out waiting for CLI process to exit") {
log.Warn("CLI process hung after kill; manual cleanup required")
// best-effort: pkill -9 the binary path
}
return err
} Prevention
- Sweep for leftover CLI processes (pgrep) after abnormal shutdowns.
- Kill the whole process group, not just the parent, when cleaning up.
- Investigate D-state hangs at the OS level instead of retrying kills.
- Watch for orphaned children holding inherited pipe fds.
When it happens
Trigger: Calling Stop on a CLI process that fails to terminate within processExitTimeout after Kill() — typically a process stuck in uninterruptible I/O or a zombie held by a child.
Common situations: CLI process blocked on network I/O in a container, child processes inheriting file descriptors keeping the process group alive, NFS/D-state hangs.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to kill CLI process
- failed waiting for CLI server to start
- ; stderr
- An in-process FFI runtime library is already loaded from
- An in-process FFI runtime library is already loaded from
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/aae72772fd767fdf.
Report an issue: GitHub.
Appendix: source
Thrown at go/client.go:2382
return fmt.Errorf("failed to kill CLI process: %w", err)
}
}
c.process = nil
return nil
}
func (c *Client) killProcessAndWait() error {
done := c.processDone
killErr := c.killProcess()
if done == nil {
return killErr
}
select {
case <-done:
return killErr
case <-time.After(processExitTimeout):
return errors.Join(killErr, fmt.Errorf("timed out waiting for CLI process to exit after kill"))
}
}
// monitorProcess signals when the CLI process exits and captures any exit error.
// processError is intentionally a local: each process lifecycle gets its own
// error value, so goroutines from previous processes can't overwrite the
// current one. Closing the channel synchronizes with readers, guaranteeing
// they see the final processError value.
func (c *Client) monitorProcess() {
done := make(chan struct{})
c.processDone = done
proc := c.process
c.osProcess.Store(proc.Process)
var processError error
c.processErrorPtr = &processError
go func() {
waitErr := proc.Wait()
var stderrOutput stringView on GitHub (pinned to cd8cf15dc3)