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

  1. Find and stop the process holding the port: `lsof -i :<port>` then kill it, and retry the login.
  2. Ensure only one OAuth login flow runs at a time (no parallel TUI instances doing login).
  3. Run in an environment that permits binding to 127.0.0.1 on that port (adjust sandbox/container/firewall settings).
  4. 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

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


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)