zeroclaw-labs/zeroclaw · error · anyhow::Error

OpenAI OAuth error: {err} ({desc})

Error message

OpenAI OAuth error: {err} ({desc})

What it means

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').

Source

Thrown at crates/zeroclaw-providers/src/auth/openai_oauth.rs:316

    let query = if let Some((_, right)) = trimmed.split_once('?') {
        right
    } else {
        trimmed
    };

    let params = parse_query_params(query);
    let is_callback_payload = trimmed.contains('?')
        || params.contains_key("code")
        || params.contains_key("state")
        || params.contains_key("error");

    if let Some(err) = params.get("error") {
        let desc = params
            .get("error_description")
            .cloned()
            .unwrap_or_else(|| "OAuth authorization failed".to_string());
        anyhow::bail!("OpenAI OAuth error: {err} ({desc})");
    }

    if let Some(expected_state) = expected_state {
        if let Some(got) = params.get("state") {
            if got != expected_state {
                anyhow::bail!("OAuth state mismatch");
            }
        } else if is_callback_payload {
            anyhow::bail!("Missing OAuth state in callback");
        }
    }

    if let Some(code) = params.get("code").cloned() {
        return Ok(code);
    }

    if !is_callback_payload {
        return Ok(trimmed.to_string());

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read ({err} ({desc})): access_denied means approval was refused — restart `auth login` and approve consent
  2. For login_required/session errors, sign in cleanly in the browser first, then redo the login flow
  3. Make sure to paste the final URL after successful consent (it should contain code=..., not error=...)
Defensive patterns

Strategy: try-catch

Validate before calling

// Cheap pre-check on the pasted URL before parsing
let query = pasted.split_once('?').map(|(_, q)| q).unwrap_or(pasted);
if query.split('&').any(|kv| kv.starts_with("error=")) {
    anyhow::bail!("redirect contains an OAuth error param — consent did not succeed; restart auth login");
}

Type guard

fn redirect_has_oauth_error(input: &str) -> bool {
    input
        .split_once('?')
        .map(|(_, q)| q)
        .unwrap_or(input)
        .split('&')
        .any(|kv| kv.starts_with("error=") || kv.starts_with("error_description="))
}

Try / catch

match openai_oauth::parse_code_from_redirect(&pasted, Some(state)) {
    Err(e) if e.to_string().starts_with("OpenAI OAuth error:") => {
        // consent failed upstream; guide user to restart rather than retry the same paste
        anyhow::bail!("authorization was not granted ({e}); re-run `zeroclaw auth login --model-provider openai-codex`");
    }
    other => other?,
}

Prevention

When it happens

Trigger: 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.

Common situations: Canceling the consent dialog and pasting the resulting URL anyway, SSO session expiry mid-consent, or policy-blocked apps producing error redirects.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/98fdd320b5625df9. Report an issue: GitHub.