github/copilot-sdk · critical

CLI process exited

Error message

CLI process exited: %w

What it means

The no-stderr variant of the process-death error: the CLI process Wait() returned an error but nothing was captured on stderr, so the client reports only 'CLI process exited: <waitErr>'. The wrapped wait error (exit status) is the only diagnostic available.

Solutions

  1. Decode the wrapped exit status: 137/SIGKILL usually means OOM — reduce workload or increase memory.
  2. Check OS logs (dmesg, journalctl) for kill events around the crash time.
  3. Enable the CLI's own logging (file-based) since stderr was empty.
  4. Add restart-on-death logic around the client lifecycle.
Defensive patterns

Strategy: retry

Try / catch

_, err := client.RPC.Call(ctx, "method", args)
if err != nil {
    var pe *os.SyscallError
    if strings.Contains(err.Error(), "CLI process exited") {
        // empty stderr: check wrapped exit status / dmesg for signal kills
        log.Errorf("CLI died silently: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: The CLI process exits with a non-zero status while producing no stderr output — e.g. killed by a signal, OOM-killed, or exiting via os.Exit with a non-zero code.

Common situations: OOM killer terminating the process (exit 137/SIGKILL), external scripts or supervisors killing it, silent fatal exits from the CLI binary.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/7a57c301dee38856. Report an issue: GitHub.

Appendix: source

Thrown at go/client.go:2408

// 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 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
	}

View on GitHub (pinned to cd8cf15dc3)