{"record":{"id":"1139e9bd79ae7a96","repo":"zeroclaw-labs/zeroclaw","slug":"xai-device-code-flow-timed-out-before-authorizatio","errorCode":null,"errorMessage":"xAI device-code flow timed out before authorization completed","messagePattern":"xAI device-code flow timed out before authorization completed","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/auth/xai_oauth.rs","lineNumber":276,"sourceCode":"            .map(|uri| require_trusted_endpoint(uri, \"complete verification URI\"))\n            .transpose()?,\n        expires_in: parsed.expires_in,\n        interval: parsed.interval.unwrap_or(5).max(1),\n    })\n}\n\npub async fn poll_device_code_tokens(\n    client: &Client,\n    token_endpoint: &str,\n    device: &DeviceCodeStart,\n) -> Result<TokenSet> {\n    let token_endpoint = require_trusted_endpoint(token_endpoint, \"token endpoint\")?;\n    let started = Instant::now();\n    let mut interval_secs = device.interval.max(1);\n\n    loop {\n        if started.elapsed() > Duration::from_secs(device.expires_in) {\n            anyhow::bail!(\"xAI device-code flow timed out before authorization completed\");\n        }\n\n        tokio::time::sleep(Duration::from_secs(interval_secs)).await;\n\n        let form = [\n            (\"grant_type\", XAI_DEVICE_CODE_GRANT_TYPE),\n            (\"device_code\", device.device_code.as_str()),\n            (\"client_id\", XAI_OAUTH_CLIENT_ID),\n        ];\n\n        let response = client\n            .post(&token_endpoint)\n            .form(&form)\n            .send()\n            .await\n            .context(\"Failed polling xAI device-code token endpoint\")?;\n\n        if response.status().is_success() {","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/xai_oauth.rs#L258-L294","documentation":"`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.","triggerScenarios":"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.","commonSituations":"Unattended terminal; user steps away; verification page queues or MFA drags on past the window.","solutions":["Restart the flow: call `start_device_code_flow` again for a fresh device_code and expiry window","Use `verification_uri_complete` when present to skip manual code entry","Display user_code immediately and prompt the user to open the verification URI right away"],"exampleFix":"// after: treat the deadline as restartable\nmatch poll_device_code_tokens(&client, &disc.token_endpoint, &device).await {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"timed out before authorization\") => {\n        let device = start_device_code_flow(&client, &disc.device_authorization_endpoint).await?;\n        poll_device_code_tokens(&client, &disc.token_endpoint, &device).await?\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match poll_device_code_tokens(&client, &disc.token_endpoint, &device).await {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"timed out before authorization\") => {\n        let device = start_device_code_flow(&client, &disc.device_authorization_endpoint).await?;\n        poll_device_code_tokens(&client, &disc.token_endpoint, &device).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Print user_code and verification_uri immediately after start","Link verification_uri_complete when present to save entry time","Never re-poll an expired DeviceCodeStart — always restart the flow"],"tags":["oauth","xai","device-code","timeout","rust"],"backgroundTag":"oauth-device-code-timeout","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}