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

{err}: {desc}

Error message

{err}: {desc}

What it means

The xAI callback query contained an `error` parameter, so `parse_code_from_redirect` fails with `{error}: {error_description}` (default description: "xAI OAuth authorization failed"). This is the IdP reporting that authorization itself failed — no code was ever issued. The classic body is `access_denied: user cancelled...`, but any redirected OAuth error surfaces here.

Source

Thrown at crates/zeroclaw-providers/src/auth/xai_oauth.rs:410

        body
    );
    let _ = stream.write_all(response.as_bytes()).await;
    Ok(code)
}

pub fn parse_code_from_redirect(input: &str, expected_state: Option<&str>) -> Result<String> {
    let trimmed = input.trim();
    if trimmed.is_empty() {
        anyhow::bail!("No xAI OAuth code provided");
    }
    let query = trimmed.split_once('?').map_or(trimmed, |(_, query)| query);
    let params = parse_query_params(query);
    if let Some(err) = params.get("error") {
        let desc = params
            .get("error_description")
            .cloned()
            .unwrap_or_else(|| "xAI OAuth authorization failed".to_string());
        anyhow::bail!("{err}: {desc}");
    }
    if let Some(expected) = expected_state {
        let actual = params
            .get("state")
            .ok_or_else(|| anyhow::Error::msg("xAI OAuth callback missing state parameter"))?;
        if actual != expected {
            anyhow::bail!("xAI OAuth state mismatch");
        }
    }
    if let Some(code) = params.get("code")
        && !code.trim().is_empty()
    {
        return Ok(code.trim().to_string());
    }
    if expected_state.is_none() && !trimmed.contains('=') && !trimmed.contains('?') {
        return Ok(trimmed.to_string());
    }
    anyhow::bail!("xAI OAuth callback missing code parameter")

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the two parts: `access_denied` is user cancellation — offer to restart; other codes indicate config or provider changes
  2. Restart the flow via `build_authorize_url` with the standard XAI_OAUTH_SCOPE
  3. For scope failures, verify the account has Grok/API access enabled
Defensive patterns

Strategy: try-catch

Type guard

fn classify_callback_error(e: &anyhow::Error) -> &'static str {
    let s = e.to_string();
    if s.starts_with("access_denied") { "user-cancelled" } else { "authorization-config-error" }
}

Try / catch

match parse_code_from_redirect(path, Some(&pkce.state)) {
    Ok(code) => exchange(code),
    Err(e) if e.to_string().starts_with("access_denied") => prompt_retry_login().await,
    Err(e) if e.to_string().contains(": ") => return Err(e), // provider-redirected error: surface it
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: The user cancels at the xAI consent page and the redirect carries `error=access_denied&error_description=...`; scope or client errors redirect with error params instead of a code.

Common situations: User backs out of consent; the account lacks permission for a requested scope (XAI_OAUTH_SCOPE includes grok-cli:access and api:access); client policy changes at the IdP.

Related errors


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