{"record":{"id":"98fdd320b5625df9","repo":"zeroclaw-labs/zeroclaw","slug":"openai-oauth-error-err-desc","errorCode":null,"errorMessage":"OpenAI OAuth error: {err} ({desc})","messagePattern":"OpenAI OAuth error: (.+?) \\((.+?)\\)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/auth/openai_oauth.rs","lineNumber":316,"sourceCode":"\n    let query = if let Some((_, right)) = trimmed.split_once('?') {\n        right\n    } else {\n        trimmed\n    };\n\n    let params = parse_query_params(query);\n    let is_callback_payload = trimmed.contains('?')\n        || params.contains_key(\"code\")\n        || params.contains_key(\"state\")\n        || params.contains_key(\"error\");\n\n    if let Some(err) = params.get(\"error\") {\n        let desc = params\n            .get(\"error_description\")\n            .cloned()\n            .unwrap_or_else(|| \"OAuth authorization failed\".to_string());\n        anyhow::bail!(\"OpenAI OAuth error: {err} ({desc})\");\n    }\n\n    if let Some(expected_state) = expected_state {\n        if let Some(got) = params.get(\"state\") {\n            if got != expected_state {\n                anyhow::bail!(\"OAuth state mismatch\");\n            }\n        } else if is_callback_payload {\n            anyhow::bail!(\"Missing OAuth state in callback\");\n        }\n    }\n\n    if let Some(code) = params.get(\"code\").cloned() {\n        return Ok(code);\n    }\n\n    if !is_callback_payload {\n        return Ok(trimmed.to_string());","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/openai_oauth.rs#L298-L334","documentation":"After splitting the pasted redirect input into query parameters, parse_code_from_redirect checks for an OAuth standard `error` parameter before looking for `code`. Its presence means the authorization server ended the flow with an error (e.g. access_denied, server_error, login_required) instead of issuing a code; the message embeds both the error code and its error_description (defaulting to 'OAuth authorization failed').","triggerScenarios":"Pasting a redirect URL like https://.../?error=access_denied&error_description=User+cancelled — the user denied consent, the session timed out at the IdP, or the authorization server failed — during `auth paste-redirect` for OpenAI.","commonSituations":"Canceling the consent dialog and pasting the resulting URL anyway, SSO session expiry mid-consent, or policy-blocked apps producing error redirects.","solutions":["Read ({err} ({desc})): access_denied means approval was refused — restart `auth login` and approve consent","For login_required/session errors, sign in cleanly in the browser first, then redo the login flow","Make sure to paste the final URL after successful consent (it should contain code=..., not error=...)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Cheap pre-check on the pasted URL before parsing\nlet query = pasted.split_once('?').map(|(_, q)| q).unwrap_or(pasted);\nif query.split('&').any(|kv| kv.starts_with(\"error=\")) {\n    anyhow::bail!(\"redirect contains an OAuth error param — consent did not succeed; restart auth login\");\n}","typeGuard":"fn redirect_has_oauth_error(input: &str) -> bool {\n    input\n        .split_once('?')\n        .map(|(_, q)| q)\n        .unwrap_or(input)\n        .split('&')\n        .any(|kv| kv.starts_with(\"error=\") || kv.starts_with(\"error_description=\"))\n}","tryCatchPattern":"match openai_oauth::parse_code_from_redirect(&pasted, Some(state)) {\n    Err(e) if e.to_string().starts_with(\"OpenAI OAuth error:\") => {\n        // consent failed upstream; guide user to restart rather than retry the same paste\n        anyhow::bail!(\"authorization was not granted ({e}); re-run `zeroclaw auth login --model-provider openai-codex`\");\n    }\n    other => other?,\n}","preventionTips":["Paste only the post-consent URL — it must contain code=, not error=","Handle access_denied/login_required descriptions as user-flow problems, not code bugs","Restart the login flow instead of re-pasting an error redirect"],"tags":["oauth2","redirect-uri","openai","consent","access-denied"],"backgroundTag":"oauth-authorization-error-redirect","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}