{"record":{"id":"00cd01e63e791272","repo":"cube-js/cube","slug":"device-code-expired-before-it-was-authorized-run","errorCode":null,"errorMessage":"device code expired before it was authorized; run `cube login` again","messagePattern":"device code expired before it was authorized; run `cube login` again","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust/cube-cli/src/oauth.rs","lineNumber":129,"sourceCode":"            \"could not parse device authorization response: {e}\\n{text}\"\n        ))\n    })\n}\n\n/// Step 3 — poll the token endpoint until the user approves (or it fails).\npub async fn poll_for_token(\n    http: &reqwest::Client,\n    url: &str,\n    cfg: &OAuthConfig,\n    device: &DeviceAuthorization,\n) -> Result<TokenResponse> {\n    let endpoint = format!(\"{}{}\", base(url), TOKEN_PATH);\n    let deadline = Instant::now() + Duration::from_secs(device.expires_in);\n    let mut interval = device.interval.max(1);\n\n    loop {\n        if Instant::now() >= deadline {\n            bail!(\"device code expired before it was authorized; run `cube login` again\");\n        }\n        tokio::time::sleep(Duration::from_secs(interval)).await;\n\n        let mut form = vec![\n            (\"grant_type\", DEVICE_CODE_GRANT),\n            (\"device_code\", device.device_code.as_str()),\n            (\"client_id\", cfg.client_id.as_str()),\n        ];\n        if let Some(secret) = &cfg.client_secret {\n            form.push((\"client_secret\", secret));\n        }\n        // Transient network failures (server redeploy, flaky connection) must\n        // not abort the login — keep polling until the device code expires.\n        let res = match http.post(&endpoint).form(&form).send().await {\n            Ok(res) => res,\n            Err(_) => continue,\n        };\n        let status = res.status();","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/rust/cube-cli/src/oauth.rs#L111-L147","documentation":"During the OAuth2 device-code flow (`cube login`), the CLI polls the token endpoint until the user completes browser authorization. This error is raised when the polling loop's deadline — computed as now + device.expires_in seconds from the device authorization response — is reached before approval. The device code is single-use and time-boxed per RFC 8628, so once it lapses the CLI cannot obtain a token and the user must restart the login.","triggerScenarios":"Raised in poll_for_token when Instant::now() >= deadline (elapsed device.expires_in seconds), or when the token endpoint returns the `expired_token` error code (see also error 672). Typical cause: the user did not open the verification URL and enter the code before expiry.","commonSituations":"User walked away after `cube login` printed the code; short expires_in from the auth server; clock skew is irrelevant here (monotonic Instant), but slow polling start after long `slow_down` backoffs (interval grows by 5s each slow_down) can consume the budget.","solutions":["Run `cube login` again and complete the browser authorization promptly","Open the verification URL and enter the device code as soon as it is displayed","Check that the auth server (url in OAuthConfig) is reachable — failed polls are retried until expiry, and persistent network failures will silently burn the whole window","If expiry happens consistently fast, verify the OAuth provider's device-code TTL configuration"],"exampleFix":"// before (expired — no token)\ncube login\n// ...waited 20 minutes...\n\n// after: re-run and authorize immediately\ncube login\n// open https://cloud.cube.dev/login/device and enter the code now","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Complete the browser authorization immediately after running `cube login`","Keep the printed verification URL open before the code expires","Ensure stable connectivity to the auth server during login so polling isn't stalled until expiry","Avoid long `slow_down` backoff chains by approving on the first or second prompt"],"tags":["oauth","device-code-flow","timeout","cli","authentication"],"backgroundTag":"oauth-device-code-expired","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}