nikivdev/code · error
device code expired. Run `f auth` again.
Error message
device code expired. Run `f auth` again.
What it means
When the poll loop receives status "expired" from the auth server — or the retry deadline elapses without approval — login aborts telling the user to run 'f auth' again. Device codes are short-lived by design; this is the normal 'user took too long to authorize' termination path.
Source
Thrown at src/auth.rs:100
if !poll_response.status().is_success() {
bail!("device auth poll failed: HTTP {}", poll_response.status());
}
let poll: DevicePollResponse = poll_response
.json()
.context("failed to parse device auth poll response")?;
match poll.status.as_str() {
"approved" => {
let token = poll
.token
.ok_or_else(|| anyhow!("device auth approved without token"))?;
env::save_ai_auth_token(token, Some(api_url.clone()))?;
println!("✓ Auth complete. You're ready to use Flow AI.");
return Ok(());
}
"pending" => continue,
"expired" => bail!("device code expired. Run `f auth` again."),
"invalid" => bail!("device code invalid. Run `f auth` again."),
other => bail!("unexpected auth status: {}", other),
}
}
bail!("device code expired. Run `f auth` again.")
}
fn open_in_browser(url: &str) {
#[cfg(target_os = "macos")]
{
let _ = std::process::Command::new("open").arg(url).status();
}
#[cfg(target_os = "linux")]
{
let _ = std::process::Command::new("xdg-open").arg(url).status();
}View on GitHub (pinned to a747e741ae)
Solutions
- Run 'f auth' again to get a fresh device code (this is the only remedy once expired)
- Authorize promptly: open the verification URL as soon as it's displayed
- Check system clock (NTP) if expiry seems premature
- Extend the client-side retry deadline if your auth server's codes last longer than the loop budget
Example fix
// before
"expired" => bail!("device code expired. Run `f auth` again."),
// after
"expired" => {
eprintln!("device code expired; requesting a new one...");
return login(api_url); // restart flow with a fresh device code
} Defensive patterns
Strategy: retry
Validate before calling
// Track elapsed time and warn the user before expiry
let deadline = Instant::now() + code_ttl;
if Instant::now() > deadline - Duration::from_secs(60) {
eprintln!("Device code is about to expire — authorize now!");
} Try / catch
match login(&api_url) {
Err(e) if e.to_string().contains("device code expired") => {
eprintln!("Code expired; starting a new login...");
return login(&api_url); // one automatic re-attempt
}
other => other,
} Prevention
- Open the verification URL immediately when shown
- Set a client-side deadline comfortably under the server's code TTL
- Sync system clock via NTP to avoid premature expiry
- Offer one automatic restart of the flow on expiry
When it happens
Trigger: Poll status equals "expired", or the loop exhausts its iterations/time budget and falls through to the final bail at the end of login.
Common situations: User waits past the code's TTL before opening the verification URL; machine clock skew makes the client stop polling early; polling loop bound too tight for slow approvals.
Related errors
- device auth start failed: HTTP {}
- device auth poll failed: HTTP {}
- MAPLE_API_TOKEN is not configured
- review failed: no review attempts were available
- codex app-server response timed out
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/a8ea2d62b49545c7.
Report an issue: GitHub.