{"record":{"id":"f278530702faf027","repo":"github/copilot-sdk","slug":"cli-process-exited-unexpectedly-nstderr-s","errorCode":null,"errorMessage":"CLI process exited unexpectedly\\nstderr: %s","messagePattern":"CLI process exited unexpectedly\\\\nstderr: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"go/client.go","lineNumber":2412,"sourceCode":"\tproc := c.process\n\tc.osProcess.Store(proc.Process)\n\tvar processError error\n\tc.processErrorPtr = &processError\n\tgo func() {\n\t\twaitErr := proc.Wait()\n\t\tvar stderrOutput string\n\t\tif buf, ok := proc.Stderr.(*truncbuffer.TruncBuffer); ok {\n\t\t\tstderrOutput = strings.TrimSpace(buf.String())\n\t\t}\n\t\tif waitErr != nil {\n\t\t\tif stderrOutput != \"\" {\n\t\t\t\tprocessError = fmt.Errorf(\"CLI process exited: %w\\nstderr: %s\", waitErr, stderrOutput)\n\t\t\t} else {\n\t\t\t\tprocessError = fmt.Errorf(\"CLI process exited: %w\", waitErr)\n\t\t\t}\n\t\t} else {\n\t\t\tif stderrOutput != \"\" {\n\t\t\t\tprocessError = fmt.Errorf(\"CLI process exited unexpectedly\\nstderr: %s\", stderrOutput)\n\t\t\t} else {\n\t\t\t\tprocessError = errors.New(\"CLI process exited unexpectedly\")\n\t\t\t}\n\t\t}\n\t\tclose(done)\n\t}()\n}\n\n// connectToServer establishes a connection to the server.\nfunc (c *Client) connectToServer(ctx context.Context) error {\n\tif c.useStdio || c.useInProcess {\n\t\t// Already connected: stdio in startCLIServer, FFI streams in startInProcess.\n\t\treturn nil\n\t}\n\n\t// Connect via TCP\n\treturn c.connectViaTCP(ctx)\n}","sourceCodeStart":2394,"sourceCodeEnd":2430,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/go/client.go#L2394-L2430","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nc := client.New(client.WithCommand(\"co-pilot\", \"serve\"))\nres, _ := c.SomeRPC(ctx, req) // may fail: CLI process exited unexpectedly\n\n// after\nif err := exec.Command(\"co-pilot\", \"serve\", \"--version\").Run(); err != nil {\n    log.Fatalf(\"CLI binary missing or broken: %v\", err)\n}\nres, err := c.SomeRPC(ctx, req)\nif err != nil {\n    select {\n    case <-c.ProcessDone():\n        c = restartClient() // process died; recreate\n    default:\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := c.SomeRPC(ctx, req)\nif err != nil {\n    select {\n    case <-c.ProcessDone():\n        // process died; inspect err for stderr tail, restart client\n    default:\n    }\n    return fmt.Errorf(\"rpc failed: %w\", err)\n}","preventionTips":["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."],"tags":["process","ipc","subprocess","go"],"backgroundTag":"process-exited-unexpectedly","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}