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

xAI device-code start failed ({status}): {body}

Error message

xAI device-code start failed ({status}): {body}

What it means

`start_device_code_flow` POSTs client_id and scope to the xAI device authorization endpoint and received a non-2xx. This is the first step of device-code login — nothing has been shown to the user yet. Status and body are embedded; common bodies are `invalid_client`, `unauthorized_client`, or rate-limit text.

Source

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

    let form = [
        ("client_id", XAI_OAUTH_CLIENT_ID),
        ("scope", XAI_OAUTH_SCOPE),
    ];

    let response = client
        .post(require_trusted_endpoint(
            device_authorization_endpoint,
            "device authorization endpoint",
        )?)
        .form(&form)
        .send()
        .await
        .context("Failed to start xAI OAuth device-code flow")?;

    if !response.status().is_success() {
        let status = response.status();
        let body = response.text().await.unwrap_or_default();
        anyhow::bail!("xAI device-code start failed ({status}): {body}");
    }

    let parsed: DeviceCodeResponse = response
        .json()
        .await
        .context("Failed to parse xAI device-code response")?;
    Ok(DeviceCodeStart {
        device_code: parsed.device_code,
        user_code: parsed.user_code,
        verification_uri: require_trusted_endpoint(&parsed.verification_uri, "verification URI")?,
        verification_uri_complete: parsed
            .verification_uri_complete
            .as_deref()
            .map(|uri| require_trusted_endpoint(uri, "complete verification URI"))
            .transpose()?,
        expires_in: parsed.expires_in,
        interval: parsed.interval.unwrap_or(5).max(1),
    })

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the embedded body: `invalid_client`/`unauthorized_client` mean device flow is unavailable for this client — switch to the browser loopback flow (`receive_loopback_code` on 127.0.0.1:56121)
  2. Back off and retry once for 429/5xx
  3. Confirm discovery succeeded and supplied a device_authorization_endpoint on an x.ai host

Example fix

// before: device flow only
let device = start_device_code_flow(&client, &disc.device_authorization_endpoint).await?;

// after: fall back to the browser flow when device start is rejected
let device = match start_device_code_flow(&client, &disc.device_authorization_endpoint).await {
    Ok(d) => d,
    Err(_) => return browser_login(&client).await,
};
Defensive patterns

Strategy: fallback

Try / catch

let device = match start_device_code_flow(&client, &disc.device_authorization_endpoint).await {
    Ok(d) => d,
    Err(e) if e.to_string().contains("device-code start failed") => {
        // device grant unavailable for this client: switch to the browser loopback flow
        return browser_login(&client).await;
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling `start_device_code_flow` with a discovered device_authorization_endpoint that rejects the request; xAI disabling device grants for the shared client; hammering the endpoint past rate limits in scripts.

Common situations: Provider-side policy changes to the device flow; automated tests repeatedly starting flows; regional blocks on xAI auth.

Related errors


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