{"record":{"id":"1fd688b36d32fb05","repo":"Hmbown/CodeWhale","slug":"oauth-callback-path-was-not","errorCode":null,"errorMessage":"OAuth callback path was not {}","messagePattern":"OAuth callback path was not (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/oauth.rs","lineNumber":1261,"sourceCode":"    }\n}\n\nfn parse_http_request_target(request_line: &str) -> Result<String> {\n    let mut parts = request_line.split_whitespace();\n    let method = parts.next().unwrap_or_default();\n    anyhow::ensure!(\n        method.eq_ignore_ascii_case(\"GET\"),\n        \"OAuth callback must be GET\"\n    );\n    let target = parts\n        .next()\n        .context(\"OAuth callback missing request target\")?;\n    Ok(target.to_string())\n}\n\nfn query_from_target<'a>(params: &OAuthProviderParams, target: &'a str) -> Result<&'a str> {\n    let path = target.split('?').next().unwrap_or(target);\n    anyhow::ensure!(\n        path == params.callback_path,\n        \"OAuth callback path was not {}\",\n        params.callback_path\n    );\n    Ok(target.split_once('?').map(|(_, q)| q).unwrap_or(\"\"))\n}\n\n/// Bind the loopback callback on both IP stacks for the first free port.\n///\n/// The redirect URI has to say `localhost` — that is what is registered with\n/// the authorization server, and redirect matching is exact — but `localhost`\n/// resolves to `::1` before `127.0.0.1` on IPv6-first hosts. Binding only\n/// IPv4 left the browser connecting to a closed port, which browsers paper\n/// over with Happy Eyeballs fallback: a working sign-in becomes a slow one,\n/// and a broken one wherever that fallback is disabled. Binding both is the\n/// fix that keeps the registered redirect URI intact.\n///\n/// A host with only one stack available binds only that one and still works.","sourceCodeStart":1243,"sourceCodeEnd":1279,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/oauth.rs#L1243-L1279","documentation":"After method validation, the callback's request target path must equal the provider's configured `callback_path`. This throw means a request reached the loopback listener whose path is not the registered OAuth redirect path, so it cannot be the expected provider callback.","triggerScenarios":"`query_from_target` sees a target whose path (before '?') differs from `params.callback_path` — e.g. `/` from a browser hitting the root, `/favicon.ico`, or a callback path changed in provider params after the auth URL was built.","commonSituations":"User truncates the redirect URL in the address bar; a browser prefetches /favicon.ico on the port; provider redirect URI configured inconsistently between the auth request and the listener.","solutions":["Use the complete redirect URL exactly as issued (including path and query)","Ensure `callback_path` in provider params matches the redirect_uri registered with the provider","Ignore/whitelist benign requests like /favicon.ico from touching the listener path check","Restart the flow if the redirect URL was edited"],"exampleFix":"// before (edited URL)\nhttp://127.0.0.1:8765/?code=abc\n// after (exact callback path preserved)\nhttp://127.0.0.1:8765/oauth/callback?code=abc","handlingStrategy":"validation","validationCode":"let path = target.split('?').next().unwrap_or(target);\nassert_eq!(path, expected_callback_path, \"unexpected callback path: {path}\");","typeGuard":"fn target_matches_path(target: &str, expected: &str) -> bool {\n    target.split('?').next().unwrap_or(target) == expected\n}","tryCatchPattern":null,"preventionTips":["Copy the redirect URL from the browser verbatim, never retype it","Keep `callback_path` consistent between the auth URL and the listener config","Return 404 for unknown paths on the listener and keep serving"],"tags":["oauth","loopback","url-validation"],"backgroundTag":"invalid-url","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}