Kuberwastaken/claurst · error

Failed to accept connection

Error message

Failed to accept connection: {}

What it means

The OAuth flow's local HTTP server failed to accept the incoming browser callback connection: `listener.accept()` returned an I/O error, which is wrapped into this message. This is an OS-level accept failure (e.g. socket closed, EMFILE too many open files), distinct from the timeout case.

Solutions

  1. Raise the file-descriptor limit (`ulimit -n 4096`) and retry the login.
  2. Check security software or container policies that interfere with loopback connections.
  3. Simply retry the OAuth flow — accept errors are usually transient.
  4. If reproducible, capture the underlying io::Error message for a bug report.

Example fix

// diagnose
// $ ulimit -n        # if low, raise before running
// $ ulimit -n 4096 && ./claurst login
Defensive patterns

Strategy: retry

Validate before calling

// Check fd headroom before starting the flow
// $ ulimit -n   (raise to >=1024 if low)

Try / catch

match run_oauth_flow(&mut app).await {
    Err(e) if e.to_string().contains("Failed to accept connection") => {
        eprintln!("Transient accept failure; retrying login once");
        run_oauth_flow(&mut app).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: `listener.accept()` inside `wait_for_callback` returns `Err(io::Error)` — the listener socket was invalidated, the process hit the file-descriptor limit, or the connection was reset at the OS level.

Common situations: Process running with `ulimit -n` exhausted (many open files/sockets); container network namespace tearing down the loopback socket; antivirus/security software killing the local connection; system suspend during the wait.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/c9a272fbfc81a9e8. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/cli/src/codex_oauth_flow.rs:121

    // Persist tokens and register an account profile in the registry.
    claurst_core::oauth_config::save_codex_tokens_and_register(&tokens, label)?;

    eprintln!("Codex login successful!");
    Ok(tokens)
}

/// Wait for OAuth callback on local server, extract code and state.
async fn wait_for_callback(listener: TcpListener) -> anyhow::Result<(String, String)> {
    use tokio::io::AsyncWriteExt;

    let (mut socket, _) = tokio::time::timeout(
        std::time::Duration::from_secs(300), // 5 minute timeout
        listener.accept(),
    )
    .await
    .map_err(|_| anyhow!("OAuth callback timeout (5 minutes)"))?
    .map_err(|e| anyhow!("Failed to accept connection: {}", e))?;

    let mut reader = BufReader::new(&mut socket);
    let mut request_line = String::new();
    reader.read_line(&mut request_line).await?;

    // Parse "GET /auth/callback?code=...&state=... HTTP/1.1"
    let parts: Vec<&str> = request_line.split_whitespace().collect();
    if parts.len() < 2 {
        bail!("Invalid HTTP request");
    }

    let path = parts[1];
    let query_start = path.find('?').ok_or_else(|| anyhow!("No query string in callback"))?;
    let query = &path[query_start + 1..];

    let mut code = String::new();
    let mut state = String::new();
    let mut error = String::new();

View on GitHub (pinned to b0637c97ec)