github/copilot-sdk · error
failed waiting for CLI server to start
Error message
failed waiting for CLI server to start: %w
What it means
The library waits for the spawned CLI server to print its port on stdout. If the caller's context is cancelled or times out before the port is reported, the process is killed and this error is returned, with the context's error (DeadlineExceeded or Canceled) embedded. Any captured stderr is appended to aid diagnosis.
Solutions
- Increase the context timeout/deadline passed to the start call.
- Inspect the appended 'stderr:' portion for the real startup failure (bad flag, port conflict, missing config).
- Ensure no caller cancels the context prematurely (e.g. request handler scopes).
- If the CLI is slow on first run due to download/auth, perform that warm-up outside the start context.
Example fix
// before client.Start(ctx) // ctx with 2s deadline // after startCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() client.Start(startCtx)
Defensive patterns
Strategy: try-catch
Validate before calling
if deadline, ok := ctx.Deadline(); ok {
if time.Until(deadline) < 30*time.Second {
return errors.New("context deadline too short for CLI server startup")
}
} Try / catch
if err := client.Start(ctx); err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
// inspect embedded stderr; retry with a longer timeout
}
return err
} Prevention
- Use a generous startup timeout (30-60s) independent of request deadlines.
- Never pass request-scoped contexts into the start call.
- Warm up the CLI (first-run downloads/auth) before the timed start.
- Log the stderr suffix — it reveals the real startup delay cause.
When it happens
Trigger: Passing a ctx that is cancelled or expires while the CLI server is still starting, e.g. a too-short context timeout around client.Start().
Common situations: Slow machine or cold-start of the CLI binary exceeding a tight deadline; HTTP request timeouts propagating their context into the start call; test suites with short timeouts.
Related errors
- CLI server process exited before reporting port
- timed out waiting for CLI process to exit after kill
- Timeout waiting for CLI server to start
- Timeout waiting for CLI to announce port
- ; stderr
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/bc9181762f896ff4.
Report an issue: GitHub.
Appendix: source
Thrown at go/client.go:2223
if err := c.process.Start(); err != nil {
return fmt.Errorf("failed to start CLI server: %w", err)
}
c.monitorProcess()
proc := c.process
scanner := bufio.NewScanner(stdout)
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()View on GitHub (pinned to cd8cf15dc3)