googleapis/mcp-toolbox · warning
graceful shutdown timed out... forcing exit
Error message
graceful shutdown timed out... forcing exit
What it means
On SIGINT/SIGTERM (ctx.Done), toolbox calls s.Shutdown with a 10-second context. If Shutdown returns context.DeadlineExceeded, graceful shutdown did not finish in time, so run() returns this error indicating a forced exit with in-flight requests possibly dropped.
Source
Thrown at cmd/root.go:530
// start watching the file(s) or folder for changes to trigger dynamic reloading
go watchChanges(ctx, watchDirs, watchedFiles, s, opts)
}
// wait for either the server to error out or the command's context to be canceled
select {
case err := <-srvErr:
if err != nil {
errMsg := fmt.Errorf("toolbox crashed with the following error: %w", err)
opts.Logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
case <-ctx.Done():
shutdownContext, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
opts.Logger.WarnContext(shutdownContext, "Shutting down gracefully...")
err := s.Shutdown(shutdownContext)
if err == context.DeadlineExceeded {
return fmt.Errorf("graceful shutdown timed out... forcing exit")
}
}
return nil
}
View on GitHub (pinned to 8cc6e09de2)
Solutions
- Ensure clients close streaming/keep-alive connections on shutdown signals
- Reduce long-running queries or add server-side timeouts
- Gracefully drain the load balancer before stopping the instance
- If this recurs, investigate stuck connections during shutdown
Defensive patterns
Strategy: fallback
Try / catch
// treat exit as shutdown-timeout and ensure cleanup
if err := cmd.Wait(); err != nil {
log.Println("graceful shutdown timed out; forcing exit")
// kill process group / release LB target before restart
} Prevention
- Drain load balancers before terminating instances
- Terminate long-lived streaming clients on SIGTERM
- Keep DB queries under 10s or add request timeouts
- Allow adequate terminationGracePeriodSeconds in k8s
When it happens
Trigger: Server shutdown exceeds 10s — typically long-lived connections (streaming MCP sessions, SSE, keep-alive HTTP connections) that prevent the server from draining.
Common situations: Kubernetes pod termination with open streaming connections; clients holding idle keep-alive connections; slow in-flight DB queries blocking request completion during shutdown.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- graceful shutdown timed out... forcing exit
- error shutting down OpenTelemetry: %w
- failed to fetch OIDC config: %w
- unable to close source %q: %w
- unable to initialize source %q: source is closed
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/0e2639c8e72700bc.
Report an issue: GitHub.