router-for-me/CLIProxyAPI · warning
failed to shutdown HTTP server: %v
Error message
failed to shutdown HTTP server: %v
What it means
Returned by the API server's Stop/Shutdown path when http.Server.Shutdown(ctx) fails. Shutdown waits for active connections to drain; it returns an error when the passed context is cancelled or times out before draining completes, or when the server is already in an invalid close state. The error wraps the underlying cause with the 'failed to shutdown HTTP server' prefix.
Source
Thrown at internal/api/server.go:394
}
}
if s.muxHTTPListener != nil {
_ = s.muxHTTPListener.Close()
}
if s.muxBaseListener != nil {
if errClose := s.muxBaseListener.Close(); errClose != nil && !errors.Is(errClose, net.ErrClosed) {
log.Debugf("failed to close shared listener: %v", errClose)
}
}
// Shutdown the HTTP server.
errShutdown := s.server.Shutdown(ctx)
if s.codexLiveHandler != nil {
s.codexLiveHandler.Close()
}
if errShutdown != nil {
return fmt.Errorf("failed to shutdown HTTP server: %v", errShutdown)
}
log.Debug("API server stopped")
return nil
}
View on GitHub (pinned to 78f0c4079e)
Solutions
- Increase or remove the deadline on the context passed to Shutdown (graceful shutdown should allow streams to finish)
- Before Shutdown, close components that hold long-lived connections (the code already closes s.codexLiveHandler; verify muxBaseListener and any WebSocket relays are closed too)
- If double-shutdown is possible, treat context.Canceled / ErrClosed as non-fatal and skip re-shutdown
- Check the wrapped error with errors.Is(err, context.DeadlineExceeded) to distinguish timeout from other failures
Example fix
// before
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err := srv.Stop(ctx)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Stop(ctx); err != nil && !errors.Is(err, context.DeadlineExceeded) {
log.Errorf("shutdown: %v", err)
} Defensive patterns
Strategy: try-catch
Try / catch
if errStop := srv.Stop(ctx); errStop != nil && !errors.Is(errStop, context.DeadlineExceeded) {
log.Errorf("server shutdown failed: %v", errStop)
}
// DeadlineExceeded with active streams is usually acceptable at process exit Prevention
- Give Shutdown a context deadline sized to your longest expected stream
- Close live handlers and WebSockets before calling Shutdown
- Guard against double shutdown with a sync.Once
When it happens
Trigger: Calling server shutdown with a context that expires (e.g. a short graceful-shutdown timeout) while streaming SSE/WebSocket/Codex live connections are still open; calling Shutdown twice or after the listener was already closed; a second Close() racing the first Shutdown.
Common situations: SIGTERM handling with an aggressive graceful timeout (1-2s) while long-lived streaming responses are active; TUI/standalone mode exiting during in-flight requests; test teardown that cancels ctx immediately.
Related errors
- antigravity token exchange: create request: %w
- antigravity userinfo: create request: %w
- failed to create token request: %w
- failed to create refresh request: %w
- kimi: failed to create device code request: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/1554a69874e6c9ce.
Report an issue: GitHub.