zeroclaw-labs/zeroclaw · warning · anyhow::Error
Device-code flow timed out before authorization completed
Error message
Device-code flow timed out before authorization completed
What it means
poll_device_code_tokens loops, sleeping device.interval seconds between token polls, and checks elapsed time against device.expires_in from the device-code response. If the user has not completed authorization before that lifetime elapses, it bails with this timeout. The device code is single-use and short-lived (typically a few minutes), so a late approval cannot be salvaged.
Source
Thrown at crates/zeroclaw-providers/src/auth/openai_oauth.rs:175
user_code: parsed.user_code,
verification_uri: parsed.verification_uri,
verification_uri_complete: parsed.verification_uri_complete,
expires_in: parsed.expires_in,
interval: parsed.interval.unwrap_or(5).max(1),
message: parsed.message,
})
}
pub async fn poll_device_code_tokens(
client: &Client,
device: &DeviceCodeStart,
) -> Result<TokenSet> {
let started = Instant::now();
let mut interval_secs = device.interval.max(1);
loop {
if started.elapsed() > Duration::from_secs(device.expires_in) {
anyhow::bail!("Device-code flow timed out before authorization completed");
}
tokio::time::sleep(Duration::from_secs(interval_secs)).await;
let form = [
("grant_type", "urn:ietf:params:oauth:grant-type:device_code"),
("device_code", device.device_code.as_str()),
("client_id", OPENAI_OAUTH_CLIENT_ID),
];
let response = client
.post(OPENAI_OAUTH_TOKEN_URL)
.form(&form)
.send()
.await
.context("Failed polling OpenAI device-code token endpoint")?;
if response.status().is_success() {View on GitHub (pinned to 88bb9c8533)
Solutions
- Restart the flow: run `zeroclaw auth login --model-provider openai-codex --device-code` again and authorize promptly at the verification URL
- Copy the user_code immediately — it expires with the device code
- If approval is slow by nature (approval queues), use the browser loopback flow instead so the redirect arrives when ready
Defensive patterns
Strategy: retry
Try / catch
match openai_oauth::poll_device_code_tokens(&client, &device).await {
Err(e) if e.to_string().contains("timed out before authorization") => {
// restart the whole flow with a fresh device code
start_device_code_flow(&client, &id, &secret, &scopes).await
}
other => other,
} Prevention
- Authorize immediately after the user_code is displayed — codes live only minutes
- Automate surfacing the verification URL (open browser, notify) so approval latency is low
- Prefer the loopback browser flow when the approver is not sitting at the terminal
When it happens
Trigger: Starting `--device-code` login, receiving the user_code/URL, and not visiting/authorizing it before expires_in; or the user approving after the poll loop already exited.
Common situations: Headless/servers where the URL is copy-pasted slowly, user steps away from the terminal, screen-sharing sessions with delays, or automation that surfaces the code too late.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- OpenAI device-code expired
- OpenAI device-code start failed ({status}): {body}
- OpenAI device-code authorization was denied
- OpenAI device-code polling failed ({status}): {}
- OpenAI device-code polling failed ({status}): {text}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/603bed723ed9b1ba.
Report an issue: GitHub.