Kuberwastaken/claurst · warning · anyhow::Error
Failed to parse OAuth callback URL
Error message
Failed to parse OAuth callback URL '{}': {} What it means
Thrown when the URL reconstructed from the callback request line ('http://{host}{path}') fails url::Url::parse. This happens when the request line lacks a path (split_whitespace().nth(1) yields None and "" is used) or the target contains characters invalid in a URL.
Solutions
- Retry the flow and ensure a real browser performs the OAuth redirect
- Keep the callback bound to 127.0.0.1 (not 0.0.0.0) so only local clients can hit it
- Inspect what is connecting to the port (netstat/lsof) if this recurs — likely a scanner or misconfigured client
- Verify no proxy rewrites the request target into a form the parser rejects
Example fix
// before: bind exposed to LAN scanners
TcpListener::bind(("0.0.0.0", 8090))
// after
TcpListener::bind(("127.0.0.1", 8090)) Defensive patterns
Strategy: retry
Try / catch
Err(e) if e.to_string().contains("parse OAuth callback URL") => {
eprintln!("non-HTTP client hit the callback port; retrying loopback-only");
run_mcp_auth_flow(server).await
} Prevention
- Bind the listener to 127.0.0.1, never 0.0.0.0
- Don't expose the callback port in containers/networks during auth
- Investigate scanners if malformed requests recur
When it happens
Trigger: wait_for_authorization_code receives a malformed request line — e.g. a bare 'GET' with no target, or a target containing raw spaces/control characters so the parsed URL is invalid.
Common situations: Non-browser clients (curl probes, health checks, port scanners) hitting the callback port and sending non-standard request lines; a proxy sending an absolute-form target; stray bot traffic.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- No query string in callback
- Bridge register: server returned
- Token exchange failed
- Token exchange failed
- API key creation failed
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/38ed1ea8ddd88176.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/oauth.rs:269
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 '{}'",
callback_path,
parsed_url.path()
);
}
if let Some(expected_state) = expected_state {
let received_state = parsed_url
.query_pairs()View on GitHub (pinned to b0637c97ec)