{"record":{"id":"ba607db47859ba3c","repo":"router-for-me/CLIProxyAPI","slug":"port-d-is-already-in-use","errorCode":null,"errorMessage":"port %d is already in use","messagePattern":"port (.+?) is already in use","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/codex/oauth_server.go","lineNumber":79,"sourceCode":"}\n\n// Start starts the OAuth callback server.\n// It sets up the HTTP handlers for the callback and success endpoints,\n// and begins listening on the specified port.\n//\n// Returns:\n//   - error: An error if the server fails to start\nfunc (s *OAuthServer) Start() error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif s.running {\n\t\treturn fmt.Errorf(\"server is already running\")\n\t}\n\n\t// Check if port is available\n\tif !s.isPortAvailable() {\n\t\treturn fmt.Errorf(\"port %d is already in use\", s.port)\n\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) {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/codex/oauth_server.go#L61-L97","documentation":"The Codex OAuth callback server (mirrors the Claude one) refuses to start because its configured callback port is already bound. The pre-start isPortAvailable probe failed, so the login flow aborts before opening the browser. Distinct from the async failure (error 199), which happens after the probe passes.","triggerScenarios":"Running Codex login while another CLIProxyAPI instance, a previous crashed process, or an unrelated service holds the callback port; two concurrent logins sharing the default port.","commonSituations":"Stale background process from an earlier login still bound; parallel logins from two terminals; the callback port colliding with a commonly used dev port; leftover container holding the port in CI.","solutions":["Identify and stop the holder: `lsof -i :<port>` / `ss -ltnp`, kill the stale process.","Avoid concurrent logins; wait for the first to finish.","Retry login with a different --oauth-callback-port.","Move the callback port away from ports your other services use."],"exampleFix":"# before\n./cli-proxy-api login --provider codex   # port held by stale process\n\n# after\nkill $(lsof -t -i :1455)\n./cli-proxy-api login --provider codex","handlingStrategy":"validation","validationCode":"ln, err := net.Listen(\"tcp\", fmt.Sprintf(\":%d\", port))\nif err != nil { return fmt.Errorf(\"codex callback port %d busy; pass --oauth-callback-port\", port) }\nln.Close()","typeGuard":null,"tryCatchPattern":"if err := server.Start(); err != nil && strings.Contains(err.Error(), \"already in use\") {\n    port = pickFreePort(); rebuildServer(port)\n}","preventionTips":["Free the callback port or use a unique one per login attempt.","Kill stale processes from previous logins before starting a new one."],"tags":["codex","oauth","port","server","auth-flow"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}