zeroclaw-labs/zeroclaw · warning · anyhow::Error
OpenAI device-code expired
Error message
OpenAI device-code expired
What it means
The token endpoint returned the OAuth error expired_token during device-code polling: the device code (and its user_code) exceeded its expires_in lifetime before authorization completed. This is the server-side counterpart of the client-side timeout (error 693) — here OpenAI itself declares the code dead.
Source
Thrown at crates/zeroclaw-providers/src/auth/openai_oauth.rs:213
}
let status = response.status();
let text = response.text().await.unwrap_or_default();
if let Ok(err) = serde_json::from_str::<OAuthErrorResponse>(&text) {
match err.error.as_str() {
"authorization_pending" => {
continue;
}
"slow_down" => {
interval_secs = interval_secs.saturating_add(5);
continue;
}
"access_denied" => {
anyhow::bail!("OpenAI device-code authorization was denied")
}
"expired_token" => {
anyhow::bail!("OpenAI device-code expired")
}
_ => {
anyhow::bail!(
"OpenAI device-code polling failed ({status}): {}",
err.error_description.unwrap_or(err.error)
)
}
}
}
anyhow::bail!("OpenAI device-code polling failed ({status}): {text}");
}
}
pub async fn receive_loopback_code(expected_state: &str, timeout: Duration) -> Result<String> {
::zeroclaw_log::scope!(
model_provider_type: "openai",
model_provider_alias: "oauth",View on GitHub (pinned to 88bb9c8533)
Solutions
- Start a fresh `zeroclaw auth login --model-provider openai-codex --device-code` and complete authorization within the displayed window
- Verify the machine clock is correct (NTP sync) so client and server lifetimes agree
- Prefer the browser loopback flow when approval latency is unpredictable
Defensive patterns
Strategy: retry
Try / catch
match openai_oauth::poll_device_code_tokens(&client, &device).await {
Err(e) if e.to_string().contains("device-code expired") => {
// server declared the code dead: fresh flow required
start_device_code_flow(&client, &id, &secret, &scopes).await
}
other => other,
} Prevention
- Keep system clocks NTP-synced so client and server lifetimes agree
- Never re-enter a user_code from an older attempt
- Complete approval in one sitting after starting the device flow
When it happens
Trigger: Polling continues (e.g. the elapsed check at the top of the loop has not tripped due to clock skew or a slow first poll) when the server reports the device code already expired — typically a user who started authorization far too late.
Common situations: Delayed approvals on headless machines, expired code pasted from an old terminal scrollback, or system clock drift between client and server.
Related errors
- Device-code flow timed out before authorization completed
- 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/a706d13693ccaacb.
Report an issue: GitHub.