{"record":{"id":"c1dccea58afe9e8d","repo":"zeroclaw-labs/zeroclaw","slug":"xai-device-code-expired","errorCode":null,"errorMessage":"xAI device-code expired","messagePattern":"xAI device-code expired","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/auth/xai_oauth.rs","lineNumber":310,"sourceCode":"            .context(\"Failed polling xAI device-code token endpoint\")?;\n\n        if response.status().is_success() {\n            return parse_token_response(response).await;\n        }\n\n        let status = response.status();\n        let text = response.text().await.unwrap_or_default();\n        if let Ok(err) = serde_json::from_str::<OAuthErrorResponse>(&text) {\n            match err.error.as_str() {\n                \"authorization_pending\" => continue,\n                \"slow_down\" => {\n                    interval_secs = interval_secs.saturating_add(5);\n                    continue;\n                }\n                \"access_denied\" | \"authorization_denied\" => {\n                    anyhow::bail!(\"xAI device-code authorization was denied\")\n                }\n                \"expired_token\" => anyhow::bail!(\"xAI device-code expired\"),\n                _ => anyhow::bail!(\n                    \"xAI device-code polling failed ({status}): {}\",\n                    err.error_description.unwrap_or(err.error)\n                ),\n            }\n        }\n        anyhow::bail!(\"xAI device-code polling failed ({status}): {text}\");\n    }\n}\n\nasync fn parse_token_response(response: reqwest::Response) -> Result<TokenSet> {\n    let status = response.status();\n    let body = response.text().await.unwrap_or_default();\n    if !status.is_success() {\n        if let Ok(err) = serde_json::from_str::<OAuthErrorResponse>(&body) {\n            anyhow::bail!(\n                \"xAI OAuth token request failed ({status}): {}\",\n                err.error_description.unwrap_or(err.error)","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/xai_oauth.rs#L292-L328","documentation":"The xAI token endpoint returned `expired_token` during device-code polling: the device_code's assigned lifetime ran out on the server side, typically because approval came too late or polling resumed after a process sleep. Distinct from the client-side elapsed-time deadline, this is the provider's verdict that the code is dead.","triggerScenarios":"User approves at the verification page after expires_in elapsed; the machine slept mid-flow so polls resume against an expired device_code.","commonSituations":"Laptop sleep during login; long verification-page delays; user returning to an old terminal session.","solutions":["Start a new flow with `start_device_code_flow` — expired codes cannot be revived","Complete approval promptly after the user_code is displayed","Avoid machine sleep while a device flow is in progress"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match poll_device_code_tokens(&client, &ep, &device).await {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"xAI device-code expired\") => {\n        let device = start_device_code_flow(&client, &dev_ep).await?; // expired codes cannot be revived\n        poll_device_code_tokens(&client, &ep, &device).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Complete approval within expires_in of the device start","Prevent machine sleep during an active flow","Handle expired_token and the client-side timeout with the same remedy: a fresh start_device_code_flow"],"tags":["oauth","xai","device-code","expiry","rust"],"backgroundTag":"oauth-device-code-expired","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}