zeroclaw-labs/zeroclaw · error

Could not parse OAuth code from input

Error message

Could not parse OAuth code from input

What it means

parse_code_from_redirect could not interpret its input at all: there is no code query parameter, and the input fails the raw-code heuristic (length > 10, no spaces, no '&'). This is the terminal failure after both parsing strategies are exhausted, meaning the user pasted something that is neither a callback URL nor a plausible authorization code.

Source

Thrown at crates/zeroclaw-providers/src/auth/gemini_oauth.rs:521

    // If we have code param, extract it
    if let Some(code) = params.get("code") {
        // Validate state if expected
        if let Some(expected) = expected_state
            && let Some(actual) = params.get("state")
            && actual != expected
        {
            anyhow::bail!("OAuth state mismatch: expected {expected}, got {actual}");
        }
        return Ok(code.clone());
    }

    // Otherwise, assume it's the raw code (if long enough and no spaces)
    if trimmed.len() > 10 && !trimmed.contains(' ') && !trimmed.contains('&') {
        return Ok(trimmed.to_string());
    }

    anyhow::bail!("Could not parse OAuth code from input")
}

/// Extract account email from Google ID token.
pub fn extract_account_email_from_id_token(id_token: &str) -> Option<String> {
    let parts: Vec<&str> = id_token.split('.').collect();
    if parts.len() != 3 {
        return None;
    }

    let payload = base64::engine::general_purpose::URL_SAFE_NO_PAD
        .decode(parts[1])
        .ok()?;

    #[derive(Deserialize)]
    struct IdTokenPayload {
        email: Option<String>,
    }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Go back to the authorize URL, complete consent, and paste the final callback URL that contains code=...
  2. If the URL contains error=access_denied instead of code, the consent was denied — restart the login and click Allow
  3. Paste just the raw authorization code (a long single token like 4/0AcvDM...) with no spaces or extra text
Defensive patterns

Strategy: validation

Validate before calling

let t = input.trim();
let looks_like_url_with_code = t.contains("code=");
let looks_like_raw_code = t.len() > 10 && !t.contains(' ') && !t.contains('&');
anyhow::ensure!(
    looks_like_url_with_code || looks_like_raw_code,
    "input is neither a callback URL with code= nor a raw code; re-copy from the browser"
);

Try / catch

match parse_code_from_redirect(input, expected_state) {
    Ok(code) => code,
    Err(e) if e.to_string() == "Could not parse OAuth code from input" => {
        eprintln!("paste the full callback URL (it must contain code=...) or just the long code value");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Pasting a consent-error URL such as http://localhost:1456/auth/callback?error=access_denied (has no code param), a natural-language sentence (contains spaces), or a short fragment under 11 characters; also pasting a URL whose query string was mangled so code= is lost.

Common situations: Google redirected with an error instead of a code (access denied, app unverified) and the user pastes that URL; clipboard contains the wrong text; user pastes the verification user_code from the device flow instead of the authorization code.

Related errors


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