Kuberwastaken/claurst · warning · anyhow::Error

Failed to read OAuth callback headers

Error message

Failed to read OAuth callback headers: {}

What it means

Thrown when read_line fails while consuming the HTTP header lines of the callback request (after the request line succeeded). The header-draining loop needs a complete header block before parsing the URL, and the socket failed mid-headers.

Solutions

  1. Retry the authentication flow
  2. Verify nothing on the host is resetting loopback connections (security agents, VPN filters)
  3. Ensure the client hitting the callback is a real browser completing the OAuth redirect
  4. Check the inner error for the specific io::Error kind and address it
Defensive patterns

Strategy: retry

Try / catch

Err(e) if e.to_string().contains("read OAuth callback headers") => {
    eprintln!("header read interrupted: {e}; retrying auth flow");
    run_mcp_auth_flow(server).await
}

Prevention

When it happens

Trigger: wait_for_authorization_code's header-reading loop hits a socket error/reset — client closed early, OS socket timeout, or a non-HTTP client sending protocol-invalid data.

Common situations: Intermittent loopback flakiness; security software resetting the connection mid-request; extremely slow client hitting an OS socket timeout.

Related errors


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

Appendix: source

Thrown at src-rust/crates/mcp/src/oauth.rs:261

) -> anyhow::Result<String> {
    let (mut socket, _) = tokio::time::timeout(Duration::from_secs(180), listener.accept())
        .await
        .map_err(|_| anyhow::anyhow!("Timeout waiting for OAuth callback"))?
        .map_err(|e| anyhow::anyhow!("Failed to accept OAuth callback connection: {}", e))?;

    let (reader, mut writer) = socket.split();
    let mut reader = BufReader::new(reader);
    let mut request_line = String::new();
    reader
        .read_line(&mut request_line)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to read OAuth callback request: {}", e))?;
    loop {
        let mut header = String::new();
        reader
            .read_line(&mut header)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to read OAuth callback headers: {}", e))?;
        if header.trim().is_empty() {
            break;
        }
    }

    let path = request_line.split_whitespace().nth(1).unwrap_or("");
    let parsed_url = url::Url::parse(&format!("http://{}{}", host, path))
        .map_err(|e| anyhow::anyhow!("Failed to parse OAuth callback URL '{}': {}", path, e))?;

    let response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\nMCP OAuth authentication finished. You can close this tab.\r\n";
    writer
        .write_all(response.as_bytes())
        .await
        .map_err(|e| anyhow::anyhow!("Failed to write OAuth callback response: {}", e))?;

    if parsed_url.path() != callback_path {
        anyhow::bail!(
            "OAuth callback path mismatch: expected '{}', got '{}'",

View on GitHub (pinned to b0637c97ec)