{"record":{"id":"38ed1ea8ddd88176","repo":"Kuberwastaken/claurst","slug":"failed-to-parse-oauth-callback-url","errorCode":null,"errorMessage":"Failed to parse OAuth callback URL '{}': {}","messagePattern":"Failed to parse OAuth callback URL '(.+?)': (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"src-rust/crates/mcp/src/oauth.rs","lineNumber":269,"sourceCode":"    let mut request_line = String::new();\n    reader\n        .read_line(&mut request_line)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to read OAuth callback request: {}\", e))?;\n    loop {\n        let mut header = String::new();\n        reader\n            .read_line(&mut header)\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to read OAuth callback headers: {}\", e))?;\n        if header.trim().is_empty() {\n            break;\n        }\n    }\n\n    let path = request_line.split_whitespace().nth(1).unwrap_or(\"\");\n    let parsed_url = url::Url::parse(&format!(\"http://{}{}\", host, path))\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse OAuth callback URL '{}': {}\", path, e))?;\n\n    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\";\n    writer\n        .write_all(response.as_bytes())\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to write OAuth callback response: {}\", e))?;\n\n    if parsed_url.path() != callback_path {\n        anyhow::bail!(\n            \"OAuth callback path mismatch: expected '{}', got '{}'\",\n            callback_path,\n            parsed_url.path()\n        );\n    }\n\n    if let Some(expected_state) = expected_state {\n        let received_state = parsed_url\n            .query_pairs()","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/mcp/src/oauth.rs#L251-L287","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: bind exposed to LAN scanners\nTcpListener::bind((\"0.0.0.0\", 8090))\n// after\nTcpListener::bind((\"127.0.0.1\", 8090))","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Err(e) if e.to_string().contains(\"parse OAuth callback URL\") => {\n    eprintln!(\"non-HTTP client hit the callback port; retrying loopback-only\");\n    run_mcp_auth_flow(server).await\n}","preventionTips":["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"],"tags":["http","url","oauth"],"backgroundTag":"invalid-url-format","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}