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

xAI device-code flow timed out before authorization complete

Error message

xAI device-code flow timed out before authorization completed

What it means

`poll_device_code_tokens` checks elapsed time against `device.expires_in` (the lifetime xAI assigned when the flow started) and bails once the budget is spent without a successful token response. This is a client-side deadline, distinct from the server's `expired_token` verdict: the user simply did not complete authorization in time.

Source

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

            .map(|uri| require_trusted_endpoint(uri, "complete verification URI"))
            .transpose()?,
        expires_in: parsed.expires_in,
        interval: parsed.interval.unwrap_or(5).max(1),
    })
}

pub async fn poll_device_code_tokens(
    client: &Client,
    token_endpoint: &str,
    device: &DeviceCodeStart,
) -> Result<TokenSet> {
    let token_endpoint = require_trusted_endpoint(token_endpoint, "token endpoint")?;
    let started = Instant::now();
    let mut interval_secs = device.interval.max(1);

    loop {
        if started.elapsed() > Duration::from_secs(device.expires_in) {
            anyhow::bail!("xAI device-code flow timed out before authorization completed");
        }

        tokio::time::sleep(Duration::from_secs(interval_secs)).await;

        let form = [
            ("grant_type", XAI_DEVICE_CODE_GRANT_TYPE),
            ("device_code", device.device_code.as_str()),
            ("client_id", XAI_OAUTH_CLIENT_ID),
        ];

        let response = client
            .post(&token_endpoint)
            .form(&form)
            .send()
            .await
            .context("Failed polling xAI device-code token endpoint")?;

        if response.status().is_success() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Restart the flow: call `start_device_code_flow` again for a fresh device_code and expiry window
  2. Use `verification_uri_complete` when present to skip manual code entry
  3. Display user_code immediately and prompt the user to open the verification URI right away

Example fix

// after: treat the deadline as restartable
match poll_device_code_tokens(&client, &disc.token_endpoint, &device).await {
    Ok(t) => t,
    Err(e) if e.to_string().contains("timed out before authorization") => {
        let device = start_device_code_flow(&client, &disc.device_authorization_endpoint).await?;
        poll_device_code_tokens(&client, &disc.token_endpoint, &device).await?
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: retry

Try / catch

match poll_device_code_tokens(&client, &disc.token_endpoint, &device).await {
    Ok(t) => t,
    Err(e) if e.to_string().contains("timed out before authorization") => {
        let device = start_device_code_flow(&client, &disc.device_authorization_endpoint).await?;
        poll_device_code_tokens(&client, &disc.token_endpoint, &device).await?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: The user starts login, receives user_code and verification_uri, and never approves before expires_in elapses; polls keep returning authorization_pending until the local clock check trips.

Common situations: Unattended terminal; user steps away; verification page queues or MFA drags on past the window.

Understand the failure class

Related errors


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