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

xAI OAuth callback missing code parameter

Error message

xAI OAuth callback missing code parameter

What it means

Thrown by parse_code_from_redirect when an xAI OAuth redirect callback parses as a query string but contains no non-empty code parameter. The error and state checks already passed (or state was not expected), so the authorization server redirected back to 127.0.0.1:56121 without ever issuing an authorization code. The loopback listener accepts exactly one connection, so whatever request hit it first is the one parsed.

Source

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

        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")
}

pub fn extract_account_id_from_jwt(token: &str) -> Option<String> {
    let payload = decode_jwt_payload(token)?;
    payload
        .get("email")
        .or_else(|| payload.get("sub"))
        .and_then(serde_json::Value::as_str)
        .map(ToString::to_string)
}

pub async fn import_grok_auth_profile(
    auth_service: &super::AuthService,
    profile: &str,
    import_path: &std::path::Path,
) -> Result<()> {
    ::zeroclaw_log::scope!(
        model_provider_type: "xai",

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Retry the xAI OAuth login command to get a fresh state and code, and complete consent in the browser this time
  2. Watch the callback URL in the browser address bar: if it lacks code=..., the consent step did not finish - confirm on the xAI page
  3. Close extra tabs and disable prefetching extensions before retrying, so nothing touches port 56121 before the real redirect
  4. If it persists, capture the exact redirect URL and verify xAI still appends code to the loopback redirect
Defensive patterns

Strategy: validation

Validate before calling

// Before accepting a redirect, confirm it actually carries a code
let query = path.split_once('?').map_or(path, |(_, q)| q);
let params = parse_query_params(query);
let has_code = params
    .get("code")
    .is_some_and(|c| !c.trim().is_empty());
if !has_code {
    // ignore this callback and keep listening for the real one
    continue;
}

Try / catch

match receive_loopback_code(&state, timeout).await {
    Ok(code) => { /* proceed to token exchange */ }
    Err(e) if e.to_string().contains("missing code parameter") => {
        // user-facing: "authorization did not complete, retry login"
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: receive_loopback_code_inner reads the first HTTP request on 127.0.0.1:56121 and extracts the path; if that path carries query parameters (contains '=' or '?') but no code=... entry, this fires. Happens when xAI redirects with only state=..., when the browser or an extension prefetches the callback URL before the real redirect, or when consent is abandoned mid-flow.

Common situations: User starts `zeroclaw` xAI OAuth login, denies or abandons the consent screen, and the browser still resolves the loopback redirect; browser prefetch or extensions hitting port 56121 first; a xAI-side change to the redirect query format.

Related errors


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