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
- 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)
- Back off and retry once for 429/5xx
- 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
- Rate-limit device-flow starts in scripts and tests
- Prefer the browser loopback flow where a display is available
- Verify discovery endpoints are x.ai hosts via require_trusted_endpoint before use
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
- xAI device-code polling failed ({status}): {}
- xAI device-code polling failed ({status}): {text}
- xAI OAuth discovery failed ({status}): {body}
- xAI device-code flow timed out before authorization complete
- xAI device-code authorization was denied
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/af56c0dd339395a4.
Report an issue: GitHub.