{"record":{"id":"1554a69874e6c9ce","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-shutdown-http-server-v","errorCode":null,"errorMessage":"failed to shutdown HTTP server: %v","messagePattern":"failed to shutdown HTTP server: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/api/server.go","lineNumber":394,"sourceCode":"\t\t}\n\t}\n\n\tif s.muxHTTPListener != nil {\n\t\t_ = s.muxHTTPListener.Close()\n\t}\n\tif s.muxBaseListener != nil {\n\t\tif errClose := s.muxBaseListener.Close(); errClose != nil && !errors.Is(errClose, net.ErrClosed) {\n\t\t\tlog.Debugf(\"failed to close shared listener: %v\", errClose)\n\t\t}\n\t}\n\n\t// Shutdown the HTTP server.\n\terrShutdown := s.server.Shutdown(ctx)\n\tif s.codexLiveHandler != nil {\n\t\ts.codexLiveHandler.Close()\n\t}\n\tif errShutdown != nil {\n\t\treturn fmt.Errorf(\"failed to shutdown HTTP server: %v\", errShutdown)\n\t}\n\n\tlog.Debug(\"API server stopped\")\n\treturn nil\n}\n","sourceCodeStart":376,"sourceCodeEnd":400,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/server.go#L376-L400","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\ndefer cancel()\nerr := srv.Stop(ctx)\n\n// after\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\nif err := srv.Stop(ctx); err != nil && !errors.Is(err, context.DeadlineExceeded) {\n\tlog.Errorf(\"shutdown: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errStop := srv.Stop(ctx); errStop != nil && !errors.Is(errStop, context.DeadlineExceeded) {\n\tlog.Errorf(\"server shutdown failed: %v\", errStop)\n}\n// DeadlineExceeded with active streams is usually acceptable at process exit","preventionTips":["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"],"tags":["shutdown","http-server","graceful-shutdown","context"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}