Kuberwastaken/claurst · error · anyhow::Error

OAuth error

Error message

OAuth error: {}

What it means

The OAuth provider redirected back with an `error` query parameter instead of an authorization code — the user denied consent, or the provider rejected the request (e.g. invalid_client, access_denied, temporarily_unavailable). The formatted value is the provider's error code.

Solutions

  1. Read the error code: access_denied means the user cancelled; restart the flow if they want to proceed
  2. For server/provider errors, wait and retry the login
  3. Verify the client_id and scopes in the auth URL are still registered with the provider
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at src-rust/crates/cli/src/codex_oauth_flow.rs:174 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/96319bbcab5d1372. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/cli/src/codex_oauth_flow.rs:174

        "<html><body style='background:#131010;color:#f1ecec;display:flex;justify-content:center;align-items:center;height:100vh;font-family:system-ui'>\
         <div style='text-align:center'><h1>Authorization Successful</h1><p>You can close this window and return to Claurst.</p></div>\
         <script>setTimeout(()=>window.close(),2000)</script></body></html>"
    } else {
        "<html><body style='background:#131010;color:#f1ecec;display:flex;justify-content:center;align-items:center;height:100vh;font-family:system-ui'>\
         <div style='text-align:center'><h1 style='color:#fc533a'>Authorization Failed</h1><p>Check the terminal for details.</p></div></body></html>"
    };
    let response = format!(
        "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
        html.len(),
        html
    );
    // Drop the BufReader so we can write back on the socket
    drop(reader);
    let _ = socket.write_all(response.as_bytes()).await;
    let _ = socket.shutdown().await;

    if !error.is_empty() {
        bail!("OAuth error: {}", error);
    }

    if code.is_empty() || state.is_empty() {
        bail!("Missing code or state in OAuth callback");
    }

    Ok((code, state))
}

/// Exchange authorization code for access tokens.
async fn exchange_code_for_tokens(code: &str, verifier: &str) -> anyhow::Result<CodexTokens> {
    let client = reqwest::Client::new();
    let params = [
        ("client_id", CODEX_CLIENT_ID),
        ("code", code),
        ("code_verifier", verifier),
        ("grant_type", "authorization_code"),
        ("redirect_uri", CODEX_REDIRECT_URI),

View on GitHub (pinned to b0637c97ec)