{"record":{"id":"c77e1d0fa116ab92","repo":"router-for-me/CLIProxyAPI","slug":"server-failed-to-start-w-c77e1d","errorCode":null,"errorMessage":"server failed to start: %w","messagePattern":"server failed to start: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/codex/oauth_server.go","lineNumber":98,"sourceCode":"\t}\n\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/auth/callback\", s.handleCallback)\n\tmux.HandleFunc(\"/success\", s.handleSuccess)\n\n\ts.server = &http.Server{\n\t\tAddr:         fmt.Sprintf(\":%d\", s.port),\n\t\tHandler:      mux,\n\t\tReadTimeout:  10 * time.Second,\n\t\tWriteTimeout: 10 * time.Second,\n\t}\n\n\ts.running = true\n\n\t// Start server in goroutine\n\tgo func() {\n\t\tif err := s.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {\n\t\t\ts.errorChan <- fmt.Errorf(\"server failed to start: %w\", err)\n\t\t}\n\t}()\n\n\t// Give server a moment to start\n\ttime.Sleep(100 * time.Millisecond)\n\n\treturn nil\n}\n\n// Stop gracefully stops the OAuth callback server.\n// It performs a graceful shutdown of the HTTP server with a timeout.\n//\n// Parameters:\n//   - ctx: The context for controlling the shutdown process\n//\n// Returns:\n//   - error: An error if the server fails to stop gracefully\nfunc (s *OAuthServer) Stop(ctx context.Context) error {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/codex/oauth_server.go#L80-L116","documentation":"The Codex callback server passed its port pre-check, but ListenAndServe — started in a goroutine — failed asynchronously and pushed the wrapped error into errorChan, where WaitForCallback surfaces it. It indicates a bind race or OS-level listen failure that occurred between the check and the actual bind, rather than the straightforward port-already-bound case.","triggerScenarios":"Another process seizes the port in the window between isPortAvailable and ListenAndServe; bind fails on a privileged port without CAP_NET_BIND_SERVICE; FD exhaustion prevents socket creation during heavy concurrent logins.","commonSituations":"Multiple CI jobs logging in simultaneously on one host racing for the same port; containers dropping capabilities so even high ports occasionally fail; systems at the FD limit.","solutions":["Read the wrapped error: EADDRINUSE → port race, retry with a fresh --oauth-callback-port; EACCES → use an unprivileged port; EMFILE → raise ulimit -n or reduce concurrent logins.","Serialize Codex login flows so ports are not contended.","For containers, ensure standard networking capabilities and sane FD limits."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"result, err := server.WaitForCallback(timeout)\nif err != nil && strings.Contains(err.Error(), \"server failed to start\") {\n    // async bind failure (EADDRINUSE race / EACCES / EMFILE) — restart on a fresh port\n    server = newServerOn(pickFreePort())\n    result, err = server.WaitForCallback(timeout)\n}","preventionTips":["Serialize login flows on a shared host to avoid port races.","Read the wrapped syscall error to pick the right fix (port vs privileges vs FD limits)."],"tags":["codex","oauth","port","server","concurrency"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}