github/copilot-sdk · critical
CLI process exited unexpectedly\nstderr
Error message
CLI process exited unexpectedly\nstderr: %s
What it means
This error is stored by the Client when the CLI subprocess terminates while the SDK expects it to keep serving the JSON-RPC connection. The client waits on the process and, once Wait() returns, records either 'CLI process exited: <wait error>' (with captured stderr appended when available) or 'CLI process exited unexpectedly' when the process died without a wait error. The error is surfaced through processDone/processErrorPtr to the JSON-RPC client so pending and new calls fail with the underlying reason.
Solutions
- Read the stderr portion of the message to find the CLI's own fatal error and fix the underlying cause (bad flag, config, or version).
- Verify the CLI binary path/version is correct and runnable (run it manually with the same args).
- Check system logs for OOM kills or external signals that terminated the process.
- Handle the error via the processDone/processError channel and restart the client with fresh state instead of reusing the dead process.
Example fix
// before
c := client.New(client.WithCommand("co-pilot", "serve"))
res, _ := c.SomeRPC(ctx, req) // may fail: CLI process exited unexpectedly
// after
if err := exec.Command("co-pilot", "serve", "--version").Run(); err != nil {
log.Fatalf("CLI binary missing or broken: %v", err)
}
res, err := c.SomeRPC(ctx, req)
if err != nil {
select {
case <-c.ProcessDone():
c = restartClient() // process died; recreate
default:
}
} Defensive patterns
Strategy: try-catch
Try / catch
res, err := c.SomeRPC(ctx, req)
if err != nil {
select {
case <-c.ProcessDone():
// process died; inspect err for stderr tail, restart client
default:
}
return fmt.Errorf("rpc failed: %w", err)
} Prevention
- Validate the CLI binary path and version at startup before issuing RPCs.
- Monitor the processDone channel instead of letting pending RPCs hang on a dead process.
- Keep stderr capture enabled so the error message includes the CLI's own fatal output.
- Set resource limits / supervision (systemd, k8s) to avoid silent OOM or signal kills.
When it happens
Trigger: Calling any Client RPC after (or while) the spawned CLI process terminates: the process crashes, is killed (OOM, SIGKILL), exits due to a startup failure, or closes stdin/stdout prematurely. Emitted at the end of the wait goroutine when waitErr is nil but the process has terminated with stderr output, or with a wrapped waitErr otherwise.
Common situations: Wrong CLI binary path or version that exits immediately; the CLI crashing on startup due to bad config; OOM killer terminating the process; user or OS sending SIGKILL/SIGTERM; CLI hitting a fatal internal error and printing a stack trace to stderr.
Related errors
- CLI server process exited before reporting port
- CLI process exited unexpectedly
- failed to gracefully shut down runtime
- failed to create stdin pipe
- failed to create stdout pipe
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/f278530702faf027.
Report an issue: GitHub.
Appendix: source
Thrown at go/client.go:2412
proc := c.process
c.osProcess.Store(proc.Process)
var processError error
c.processErrorPtr = &processError
go func() {
waitErr := proc.Wait()
var stderrOutput string
if buf, ok := proc.Stderr.(*truncbuffer.TruncBuffer); ok {
stderrOutput = strings.TrimSpace(buf.String())
}
if waitErr != nil {
if stderrOutput != "" {
processError = fmt.Errorf("CLI process exited: %w\nstderr: %s", waitErr, stderrOutput)
} else {
processError = fmt.Errorf("CLI process exited: %w", waitErr)
}
} else {
if stderrOutput != "" {
processError = fmt.Errorf("CLI process exited unexpectedly\nstderr: %s", stderrOutput)
} else {
processError = errors.New("CLI process exited unexpectedly")
}
}
close(done)
}()
}
// connectToServer establishes a connection to the server.
func (c *Client) connectToServer(ctx context.Context) error {
if c.useStdio || c.useInProcess {
// Already connected: stdio in startCLIServer, FFI streams in startInProcess.
return nil
}
// Connect via TCP
return c.connectViaTCP(ctx)
}View on GitHub (pinned to cd8cf15dc3)