googleapis/mcp-toolbox · warning
graceful shutdown timed out... forcing exit
Error message
graceful shutdown timed out... forcing exit
What it means
On context cancellation runServe attempts a graceful shutdown via s.Shutdown(shutdownContext) with a 10-second timeout. If Shutdown returns context.DeadlineExceeded — in-flight requests/hanging connections prevent completion — this error is returned, signaling a forced exit.
Source
Thrown at cmd/internal/serve/command.go:133
}
}()
}
// 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 long-lived connections promptly on server shutdown signal
- Check for hung database queries/cancellations that block connection cleanup
- Increase grace period where the platform allows, or force-close idle connections if the server API exposes it
- Log and restart; this is the intended last-resort path and is mostly benign
Defensive patterns
Strategy: retry
Try / catch
err := s.Shutdown(shutdownContext)
if err == context.DeadlineExceeded {
// forced exit after grace period — log and proceed
logger.Warn("graceful shutdown timed out; forcing exit")
} Prevention
- Keep the default 10s grace period or extend it for streaming workloads
- Ensure clients disconnect on SIGTERM (proper MCP/SSE client lifecycle)
- Investigate hung DB queries that block connection drain
When it happens
Trigger: s.Shutdown(shutdownContext) does not complete within the 10-second shutdownContext deadline because long-lived connections (streaming SSE/MCP sessions, websockets, open DB queries) never drain.
Common situations: Kubernetes SIGTERM with long-lived MCP streaming connections that don't close, clients keeping keep-alive connections open, or a hung database query blocking drain.
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
- toolbox failed to initialize: %w
- toolbox failed to start listener: %w
- toolbox crashed with the following error: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/dd8cebbb01822720.
Report an issue: GitHub.