Kuberwastaken/claurst · error
Failed to bind port
Error message
Failed to bind port {}: {} What it means
The Codex OAuth flow binds a local TCP listener on 127.0.0.1:CODEX_OAUTH_PORT to receive the browser callback. If the OS refuses the bind, the flow cannot receive the authorization code and fails with this error wrapping the underlying io::Error (e.g. EADDRINUSE, EACCES).
Solutions
- Find and stop the process holding the port: `lsof -i :<port>` then kill it, and retry the login.
- Ensure only one OAuth login flow runs at a time (no parallel TUI instances doing login).
- Run in an environment that permits binding to 127.0.0.1 on that port (adjust sandbox/container/firewall settings).
- If it persists, reboot or wait for TIME_WAIT sockets to clear, then retry.
Example fix
// before
let listener = TcpListener::bind(format!("127.0.0.1:{}", CODEX_OAUTH_PORT)).await
.map_err(|e| anyhow!("Failed to bind port {}: {}", CODEX_OAUTH_PORT, e))?;
// after: check/free the port first
// $ lsof -ti :1455 | xargs kill (then retry; or run the flow once at a time) Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check port availability before starting the flow (bash)
ss -ltn | grep -q ":1455 " && { echo "port 1455 in use; close the other login"; exit 1; } Try / catch
match run_oauth_flow(&mut app).await {
Err(e) if e.to_string().contains("Failed to bind port") => {
eprintln!("Another login may be running; free the port and retry");
}
other => other?,
} Prevention
- Run only one OAuth login flow at a time.
- Kill stale processes from previous failed logins (lsof -i :<port>).
- Ensure your sandbox/container allows binding to loopback ports.
When it happens
Trigger: `TcpListener::bind("127.0.0.1:<CODEX_OAUTH_PORT>")` fails inside `run_oauth_flow_with_label`: the port is already in use by another OAuth flow instance, a leftover process still holds the socket, or a firewall/security policy blocks binding to the port.
Common situations: Running two login flows concurrently; a previous login attempt crashed leaving a zombie process holding the port; running in a container/sandbox that disallows the port; port reserved by another local dev server.
Related errors
- Failed to accept connection
- Failed to bind OAuth callback listener on
- models endpoint returned
- Bridge register: server returned
- start_bridge: bridge is not active
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/97036e18af6e3fe0.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/cli/src/codex_oauth_flow.rs:84
/// launch fails.
pub async fn run_oauth_flow(event_tx: mpsc::Sender<DeviceAuthEvent>) -> anyhow::Result<CodexTokens> {
run_oauth_flow_with_label(event_tx, None).await
}
/// Same as [`run_oauth_flow`] but lets the caller supply a label for the
/// newly registered profile.
pub async fn run_oauth_flow_with_label(
event_tx: mpsc::Sender<DeviceAuthEvent>,
label: Option<&str>,
) -> anyhow::Result<CodexTokens> {
let verifier = generate_code_verifier();
let challenge = compute_code_challenge(&verifier);
let state = generate_state();
// Bind local server for callback
let listener = TcpListener::bind(format!("127.0.0.1:{}", CODEX_OAUTH_PORT))
.await
.map_err(|e| anyhow!("Failed to bind port {}: {}", CODEX_OAUTH_PORT, e))?;
let auth_url = build_auth_url(&challenge, &state);
// Send the URL to the TUI so it can display + clipboard-copy it.
let _ = event_tx.send(DeviceAuthEvent::GotBrowserUrl { url: auth_url.clone() }).await;
// Also try to open the browser (best-effort; may silently fail in headless envs).
let _ = open::that(&auth_url);
// Wait for OAuth callback
let (code, callback_state) = wait_for_callback(listener).await?;
if callback_state != state {
bail!("OAuth state mismatch — possible CSRF attack");
}
// Exchange code for tokens
let tokens = exchange_code_for_tokens(&code, &verifier).await?;
View on GitHub (pinned to b0637c97ec)