jdx/mise · error
GitHub device authorization expired
Error message
GitHub device authorization expired
What it means
During the GitHub OAuth device-flow polling loop, poll_access_token enforces a deadline derived from the device-code response (github_in / expires_in). If GitHub keeps returning authorization_pending past that deadline, mise bails with 'GitHub device authorization expired' instead of polling forever. This mirrors GitHub's own expired_token error but is enforced locally.
Source
Thrown at src/github/oauth.rs:329
.error_for_status()?
.json::<DeviceCodeResponse>()
.await?)
}
async fn poll_access_token(device: &DeviceCodeResponse) -> Result<TokenResponse> {
let settings = Settings::get();
let deadline = chrono::Utc::now() + chrono::Duration::seconds(device.expires_in as i64);
let mut interval = device.interval.max(1);
let url = format!(
"{}/oauth/access_token",
settings.github.oauth_auth_url.trim_end_matches('/')
);
let client_id = settings.github.oauth_client_id.trim();
let client = crate::http::HTTP.reqwest()?;
loop {
if chrono::Utc::now() >= deadline {
bail!("GitHub device authorization expired");
}
tokio::time::sleep(Duration::from_secs(interval)).await;
let response = match client
.post(&url)
.header("Accept", "application/json")
.form(&[
("client_id", client_id),
("device_code", device.device_code.as_str()),
("grant_type", GRANT_DEVICE_CODE),
])
.send()
.await
.and_then(|r| r.error_for_status())
{
Ok(resp) => match resp.json::<TokenResponse>().await {
Ok(body) => body,
Err(err) => {View on GitHub (pinned to afd2eddd3a)
Solutions
- Restart the flow: run `mise token github --oauth` again to get a fresh device code and enter it promptly.
- Open https://github.com/login/device as soon as the code is printed and complete authorization before the code expires.
- Avoid suspending the machine or killing the process mid-authorization; if interrupted, start over.
- If authorization is impossible in this environment, use a GITHUB_TOKEN environment variable instead of OAuth.
Example fix
// before # started `mise token github --oauth`, entered code 20 minutes later // GitHub device authorization expired // after # rerun and enter the code immediately at https://github.com/login/device mise token github --oauth
Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
loop {
match token_async(req).await {
Err(e) if e.to_string().contains("device authorization expired") => {
if attempts >= 1 { return Err(e); }
attempts += 1; // restart the flow with a fresh code
}
Ok(t) => break Ok(t),
Err(e) => break Err(e),
}
} Prevention
- Enter the device code at github.com/login/device as soon as it is printed.
- Do not suspend the machine or kill the process during authorization.
- Keep polling under the code's validity window; avoid artificial long sleeps in wrappers.
- Only run one device-flow authorization at a time.
When it happens
Trigger: poll_access_token (via token_async) polls the GitHub oauth/access_token endpoint in a loop; every iteration where chrono::Utc::now() >= deadline bails. Triggered when the user does not enter the code within the device code's validity window (typically ~15 minutes), or polling stalls/sleeps long enough (slow_down increments) to blow past the deadline.
Common situations: User walks away during `mise token github --oauth` and enters the code after it expired; machine suspended mid-poll; very slow network repeatedly delaying polls past the window.
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
- GitHub device authorization was denied
- {details}
- GitHub OAuth is not configured. Set github.oauth_client_id f
- GitHub OAuth is configured for {}, not {}
- GitHub OAuth token is not cached. Run `mise token github --o
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/4155620bce6ecba1.
Report an issue: GitHub.