Hmbown/CodeWhale · error
{}
Error message
{} What it means
The OAuth device-code polling loop (`DeviceCodePoller::run`) reached its lifetime deadline before the authorization flow completed, so it gives up with a timeout error. The message is the poller's configured `timeout_message`, or the `slow_down_timeout_message` instead when the server sent at least one `slow_down` response (a hint of client clock drift, e.g. in WSL/VMs). This is a deliberate terminal condition, not a transport failure.
Solutions
- Complete the device-code authorization in the browser promptly and restart the login flow.
- Increase the poller's `lifetime` (builder setting) to give users more time.
- If the slow_down-specific message appears, resync the system clock (e.g. restart WSL or run an NTP sync) and retry.
- Check network access to the auth server so polls actually reach Pending/Complete instead of stalling.
Example fix
// before DeviceCodePoller::new(endpoint).lifetime(Duration::from_secs(60)) // after DeviceCodePoller::new(endpoint).lifetime(Duration::from_secs(600))
Defensive patterns
Strategy: try-catch
Validate before calling
let lifetime = Duration::from_secs(600); assert!(lifetime > Duration::from_secs(60), "device-code lifetime too short for interactive login");
Try / catch
match poller.run(sleep, poll) {
Err(err) if err.to_string().contains("timed out") => eprintln!("Login not completed in time; rerun and authorize promptly."),
Err(err) => return Err(err),
Ok(token) => store(token),
} Prevention
- Configure a generous lifetime (several minutes) for interactive logins.
- Surface the verification URL prominently so users act quickly.
- Watch for slow_down-shaped messages and prompt an NTP/clock resync in WSL/VM environments.
- Treat this error as retryable: offer to restart the flow instead of failing hard.
When it happens
Trigger: Calling `DeviceCodePoller::run(sleep, poll)` when the user never completes browser authorization (or the token endpoint keeps returning Pending/SlowDown) before `lifetime` elapses; also when `wait_before_first_poll` is set and the remaining lifetime is already zero.
Common situations: User abandons the login flow in the browser; user takes too long to enter the device code; system clock drifts badly inside WSL or a suspended VM causing repeated `slow_down` responses until the deadline passes; `lifetime` configured too short for slow users.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- {message}
- {timeout_message}
- returned an unusable verification URI
- offers no device-code flow; sign in through the browser…
- MCP server rejected the request with and refreshing the…
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/aec415307d2d45c7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/device_code.rs:174
};
}
}
// Never sleep past the code's expiry, even after slow_down backoff.
let remaining = deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
break;
}
sleep(interval.min(remaining));
}
Err(self.timed_out(saw_slow_down))
}
fn timed_out(&self, saw_slow_down: bool) -> anyhow::Error {
match (saw_slow_down, self.slow_down_timeout_message.as_deref()) {
(true, Some(message)) => anyhow::anyhow!("{message}"),
_ => anyhow::anyhow!("{}", self.timeout_message),
}
}
}
/// Reject a device-code verification URI that must not be handed to a browser
/// opener.
///
/// Ported from pi's `validateVerificationUri`
/// (`packages/ai/src/auth/oauth/xai.ts`, MIT, Copyright (c) 2025 Mario
/// Zechner): the URI comes straight off the wire and is passed to the platform
/// "open this" call, so a malicious or compromised response could otherwise
/// launch `file:`, a custom app scheme, or a helper with attacker-chosen
/// arguments. pi requires `https:`; Codewhale additionally allows `http:` on a
/// loopback host, which is what self-hosted issuers and the device-code tests
/// use — matching the loopback allowance the account login already makes.
///
/// Embedded credentials are rejected in every case.
pub fn validate_browser_verification_uri(raw: &str, context: &str) -> Result<String> {View on GitHub (pinned to 433685b202)