github/copilot-sdk · warning
timed out gracefully shutting down runtime after
Error message
timed out gracefully shutting down runtime after %s
What it means
CopilotClient.Stop gives the runtime a fixed window (runtimeShutdownTimeout) to shut down gracefully. If the runtime does not finish in time, Stop appends this timeout error instead of waiting indefinitely.
Solutions
- Ensure sessions/tool calls are completed or cancelled before Stop.
- Check runtime logs for what is blocking shutdown.
- Increase runtimeShutdownTimeout if legitimately slow, or accept the timeout and force-terminate the process.
- Upgrade to a runtime version with faster shutdown.
Example fix
// before
client.Stop(ctx) // times out silently-ish
// after
if err := client.Stop(ctx); err != nil {
if strings.Contains(err.Error(), "timed out gracefully shutting down runtime") {
process.Kill() // force-terminate hung runtime
}
} Defensive patterns
Strategy: try-catch
Try / catch
if err := client.Stop(ctx); err != nil && strings.Contains(err.Error(), "timed out gracefully") { process.Kill() } Prevention
- Cancel in-flight tool calls before Stop
- Budget adequate shutdown time
- Monitor for hung runtimes
When it happens
Trigger: Calling Stop when the runtime takes longer than runtimeShutdownTimeout to respond to runtime.shutdown.
Common situations: Runtime blocked on open tool calls or slow cleanup; heavily loaded host; large session state being torn down.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to disconnect session
- failed to gracefully shut down runtime
- runtime.shutdown timed out during Client::stop
- Factory limit "timeoutSeconds" must not exceed
- Timeout waiting for CLI to announce port
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/0634ba30372c8dea.
Report an issue: GitHub.
Appendix: source
Thrown at go/client.go:614
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)
}
}
c.process = nil
// Tear down the in-process FFI host (closes the connection and shuts down the
// native runtime). No child process to reap in this mode.
if c.ffiHost != nil {View on GitHub (pinned to cd8cf15dc3)