github/copilot-sdk · error
CLI server process exited before reporting port
Error message
CLI server process exited before reporting port
What it means
While waiting for the CLI server to report its port, the process terminated on its own (processDone channel closed). The library surfaces a base error that is enriched with captured stderr (if any) and joined with the kill error. This indicates the CLI server crashed or exited early during startup.
Solutions
- Inspect the joined stderr in the returned error to find the process's own failure message
- Verify the runtime binary path/COPILOT_CLI_PATH points to a working, version-compatible executable
- Check for sandbox/memory limits killing the child process and increase them
- Retry startup; if persistent, reinstall or rebuild the runtime bundle
Defensive patterns
Strategy: retry
Validate before calling
if _, err := os.Stat(cliPath); err != nil {
return fmt.Errorf("CLI server binary not found at %s", cliPath)
} Try / catch
if err := client.Start(ctx); err != nil {
if strings.Contains(err.Error(), "exited before reporting port") {
log.Errorf("CLI server failed to start: %v", err) // stderr is embedded
// inspect stderr / retry once with backoff
}
} Prevention
- Log the stderr portion of the joined error at startup
- Verify binary/runtime versions match the SDK before deploy
- Watch for sandbox or resource limits that kill child processes
When it happens
Trigger: Starting a Client whose CLI server subprocess dies before printing its listening port — bad binary, missing runtime assets, fatal startup error in stderr, port/binding failure.
Common situations: Incompatible or corrupt copilot binary; runtime.node missing; the process being OOM-killed or killed by a sandbox; a stale/incompatible binary version after an upgrade.
Related errors
- CLI process exited unexpectedly
- CLI process exited unexpectedly. stderr
- 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/aaff5f98916b8d46.
Report an issue: GitHub.
Appendix: source
Thrown at go/client.go:2232
portRegex := regexp.MustCompile(`listening on port (\d+)`)
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
for {
select {
case <-ctx.Done():
killErr := c.killProcess()
baseErr := fmt.Errorf("failed waiting for CLI server to start: %w", ctx.Err())
if buf, ok := proc.Stderr.(*truncbuffer.TruncBuffer); ok {
if stderr := strings.TrimSpace(buf.String()); stderr != "" {
baseErr = fmt.Errorf("%w; stderr: %s", baseErr, stderr)
}
}
return errors.Join(baseErr, killErr)
case <-c.processDone:
killErr := c.killProcess()
baseErr := errors.New("CLI server process exited before reporting port")
if buf, ok := proc.Stderr.(*truncbuffer.TruncBuffer); ok {
if stderr := strings.TrimSpace(buf.String()); stderr != "" {
baseErr = fmt.Errorf("%w; stderr: %s", baseErr, stderr)
}
}
return errors.Join(baseErr, killErr)
default:
if scanner.Scan() {
line := scanner.Text()
if matches := portRegex.FindStringSubmatch(line); len(matches) > 1 {
port, err := strconv.Atoi(matches[1])
if err != nil {
killErr := c.killProcess()
return errors.Join(fmt.Errorf("failed to parse port: %w", err), killErr)
}
c.actualPort = port
return nil
}View on GitHub (pinned to cd8cf15dc3)