github/copilot-sdk · error
failed to gracefully shut down runtime
Error message
failed to gracefully shut down runtime: %w
What it means
During CopilotClient.Stop, the SDK requests a graceful runtime shutdown and waits for a result. If the shutdown goroutine returns an error, Stop appends this wrapped error, indicating the runtime rejected or failed the shutdown request.
Solutions
- Read the wrapped cause for the runtime's shutdown error.
- Verify the runtime process is responsive (logs, health check).
- If shutdown repeatedly fails, force-kill the runtime process as a last resort.
- Upgrade SDK/runtime if the error indicates a known shutdown bug.
Example fix
// before
client.Stop(ctx) // ignore partial shutdown errors
// after
if err := client.Stop(ctx); err != nil {
log.Printf("runtime shutdown issue: %v", err)
// consider force-killing the runtime process
} Defensive patterns
Strategy: try-catch
Try / catch
if err := client.Stop(ctx); err != nil { if strings.Contains(err.Error(), "gracefully shut down runtime") { /* check runtime process */ } } Prevention
- Ensure runtime process is responsive before Stop
- Watch runtime logs during shutdown
- Keep SDK/runtime versions matched
When it happens
Trigger: Calling Stop when the runtime.shutdown RPC returns an error (e.g. runtime in a bad state, request failed mid-flight).
Common situations: Runtime hung or wedged; JSON-RPC connection broken during shutdown; runtime binary crash during cleanup.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- CLI server process exited before reporting port
- CLI process exited unexpectedly
- failed to disconnect session
- timed out gracefully shutting down runtime after
- failed to create stdin pipe
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/c28b553b1c3d5a67.
Report an issue: GitHub.
Appendix: source
Thrown at go/client.go:608
c.clearGitHubTokenProviders()
c.startStopMux.Lock()
defer c.startStopMux.Unlock()
if (c.process != nil || c.ffiHost != nil) && !c.isExternalServer && c.RPC != nil {
rpcClient := c.RPC
runtimeShutdownStart := time.Now()
shutdownDone := make(chan error, 1)
go func() {
_, err := rpcClient.Runtime.Shutdown(context.Background())
shutdownDone <- err
}()
select {
case err := <-shutdownDone:
if err != nil {
c.logDebugTiming(runtimeShutdownStart, "CopilotClient.Stop runtime shutdown failed")
errs = append(errs, fmt.Errorf("failed to gracefully shut down runtime: %w", err))
} else {
c.logDebugTiming(runtimeShutdownStart, "CopilotClient.Stop runtime shutdown complete")
}
case <-time.After(runtimeShutdownTimeout):
c.logDebugTiming(runtimeShutdownStart, "CopilotClient.Stop runtime shutdown timed out")
errs = append(errs, fmt.Errorf("timed out gracefully shutting down runtime after %s", runtimeShutdownTimeout))
}
}
// The runtime completes all cleanup before responding to runtime.shutdown
// and then leaves termination to us; it deliberately keeps its JSON-RPC
// server alive to send the response and never self-exits. Waiting for a
// self-exit that will never come just wastes time, so terminate the child
// immediately and only wait to reap it.
if c.process != nil && !c.isExternalServer {
if err := c.killProcessAndWait(); err != nil {
errs = append(errs, err)
}View on GitHub (pinned to cd8cf15dc3)