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

  1. Increase or remove the deadline on the context passed to Shutdown (graceful shutdown should allow streams to finish)
  2. Before Shutdown, close components that hold long-lived connections (the code already closes s.codexLiveHandler; verify muxBaseListener and any WebSocket relays are closed too)
  3. If double-shutdown is possible, treat context.Canceled / ErrClosed as non-fatal and skip re-shutdown
  4. 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

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


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/1554a69874e6c9ce. Report an issue: GitHub.