{"record":{"id":"10d27c69defcf9ec","repo":"Kuberwastaken/claurst","slug":"redirect-uri-is-missing-host","errorCode":null,"errorMessage":"Redirect URI '{}' is missing host","messagePattern":"Redirect URI '(.+?)' is missing host","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-rust/crates/mcp/src/oauth.rs","lineNumber":221,"sourceCode":"    );\n\n    Ok(McpAuthSession {\n        server_name: server_name.to_string(),\n        auth_url,\n        redirect_uri,\n        verifier,\n        metadata,\n    })\n}\n\nasync fn bind_callback_listener(\n    redirect_uri: &str,\n) -> anyhow::Result<(TcpListener, String, String)> {\n    let redirect_url = url::Url::parse(redirect_uri)\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse redirect URI '{}': {}\", redirect_uri, e))?;\n    let host = redirect_url\n        .host_str()\n        .ok_or_else(|| anyhow::anyhow!(\"Redirect URI '{}' is missing host\", redirect_uri))?\n        .to_string();\n    let port = redirect_url\n        .port_or_known_default()\n        .ok_or_else(|| anyhow::anyhow!(\"Redirect URI '{}' is missing port\", redirect_uri))?;\n    let callback_path = if redirect_url.path().is_empty() {\n        \"/callback\".to_string()\n    } else {\n        redirect_url.path().to_string()\n    };\n    let listener = TcpListener::bind((host.as_str(), port))\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to bind OAuth callback listener on {}:{}: {}\", host, port, e))?;\n\n    Ok((listener, host, callback_path))\n}\n\nasync fn wait_for_authorization_code(\n    listener: TcpListener,","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/mcp/src/oauth.rs#L203-L239","documentation":"The OAuth redirect_uri parsed successfully as a URL but contains no host component (e.g. a relative or scheme-only URI). The callback listener needs a host to bind a TCP socket on, so the flow aborts.","triggerScenarios":"Passing a scheme-only or non-hierarchical URI, e.g. 'http:///callback', 'mailto:foo@bar.com', or 'about:blank' as the redirect_uri to run_mcp_auth_session.","commonSituations":"Config where the host part was accidentally deleted ('http:///callback'); using a custom-scheme redirect (com.example.app:/oauth) with a loopback-based flow; corrupted config merges dropping the host.","solutions":["Ensure the redirect URI includes a host, e.g. http://127.0.0.1:PORT/callback","Use 127.0.0.1 or localhost as the host for loopback OAuth callbacks","Check settings.json / env for a mangled URI missing the host segment","Only use this flow with http(s) redirect URIs, not custom app schemes"],"exampleFix":"// before\nlet redirect_uri = \"http:///callback\";\n// after\nlet redirect_uri = \"http://127.0.0.1:8080/callback\";","handlingStrategy":"validation","validationCode":"fn has_host(uri: &str) -> bool {\n    url::Url::parse(uri).map(|u| u.host_str().is_some()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use http://127.0.0.1:PORT/callback as the canonical loopback redirect shape","Never use custom app schemes (myapp:/) with loopback callback flows","Validate config URIs after merges/edits"],"tags":["oauth","url","config"],"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"}