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
- Retry the authentication flow
- Verify nothing on the host is resetting loopback connections (security agents, VPN filters)
- Ensure the client hitting the callback is a real browser completing the OAuth redirect
- 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
- Retry transient loopback read failures
- Check for VPN/firewall software that resets local connections
- Ensure only a real browser hits the callback endpoint
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
- API key creation failed
- Bridge register: server returned
- exchange_code: HTTP
- Failed to read OAuth callback request
- refresh: HTTP
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)