{"record":{"id":"e21456bcae15ed73","repo":"Kuberwastaken/claurst","slug":"redirect-uri-is-missing-port","errorCode":null,"errorMessage":"Redirect URI '{}' is missing port","messagePattern":"Redirect URI '(.+?)' is missing port","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-rust/crates/mcp/src/oauth.rs","lineNumber":225,"sourceCode":"        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,\n    host: &str,\n    callback_path: &str,\n    expected_state: Option<&str>,\n) -> anyhow::Result<String> {","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/mcp/src/oauth.rs#L207-L243","documentation":"Thrown in bind_callback_listener when port_or_known_default() returns None — only possible for URLs whose scheme has no default port and which carry no explicit port. The library must bind a concrete local port for the OAuth callback.","triggerScenarios":"Passing a redirect URI with a scheme lacking a default port and no :port, e.g. 'ftp://host/callback' or a malformed/custom scheme; with http/https URIs this cannot trigger since defaults (80/443) exist.","commonSituations":"Copy-pasting a redirect URI with a typo'd scheme; constructing the URI from parts and omitting the port with a non-http scheme.","solutions":["Use an explicit port in the redirect URI: http://127.0.0.1:PORT/callback","Use the http scheme so the default port logic applies","Ensure the port matches what the OAuth provider registered for the redirect","Fix the URI scheme typo if the scheme was mangled"],"exampleFix":"// before\nlet redirect_uri = \"myapp://127.0.0.1/callback\";\n// after\nlet redirect_uri = \"http://127.0.0.1:8080/callback\";","handlingStrategy":"validation","validationCode":"fn has_port(uri: &str) -> bool {\n    url::Url::parse(uri).map(|u| u.port_or_known_default().is_some()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always specify an explicit port: http://127.0.0.1:PORT/callback","Stick to the http scheme so a default port always exists","Match the registered port at the OAuth provider"],"tags":["oauth","url","network"],"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"}