chatboxai/chatbox · warning · Error
Device flow timed out
Error message
Device flow timed out
What it means
Thrown when the device-flow polling loop in waitForToken() reaches its 10-minute hard deadline (Date.now() + 10*60*1000) without ever receiving an access_token and without GitHub returning a terminal error. It is a guard against the user simply never completing authorization. The finally block clears pendingDeviceCode, so the flow must be restarted from scratch.
Source
Thrown at src/main/oauth/providers/github-copilot.ts:128
// Use the GitHub access token directly as the Copilot API credential
// (same approach as openllmprovider)
return {
accessToken: raw.access_token,
// No refresh token — the GitHub access token doesn't expire
// but the Copilot API session may need re-auth periodically
}
}
if (raw.error === 'authorization_pending') continue
if (raw.error === 'slow_down') {
intervalMs += 5000
continue
}
throw new Error(`Device flow failed: ${raw.error}`)
}
throw new Error('Device flow timed out')
} finally {
pendingDeviceCode = null
}
},
async refreshToken(credentials) {
// GitHub access tokens from device flow don't expire in the traditional sense.
// Just return the existing credentials.
return credentials
},
}
View on GitHub (pinned to 81571269ad)
Solutions
- Catch this specific timeout and restart via startDeviceFlow() to issue a fresh device_code and user_code.
- Surface a clear UI message ('Login timed out — click to try again') rather than a generic error.
- Consider bumping the deadline if GitHub's documented device-code lifetime exceeds 10 minutes.
Example fix
// before
const creds = await provider.waitForToken(signal)
// after
try {
const creds = await provider.waitForToken(signal)
return creds
} catch (e) {
if (/timed out/i.test(String(e))) {
const restarted = await provider.startDeviceFlow()
throw new ReauthRequiredError(restarted)
}
throw e
} Defensive patterns
Strategy: retry
Type guard
function isDeviceFlowTimeout(e: unknown): boolean {
return e instanceof Error && /Device flow timed out/i.test(e.message)
} Try / catch
try {
return await provider.waitForToken(signal)
} catch (e) {
if (isDeviceFlowTimeout(e)) {
// restart with a fresh device code + user code
const fresh = await provider.startDeviceFlow()
throw new ReauthRequiredError(fresh)
}
throw e
} Prevention
- Surface an in-app countdown so the user knows authorization is time-limited.
- Restart startDeviceFlow() automatically (with user consent) when this timeout fires.
- If timeouts are chronic, verify the deadline (10 min) vs GitHub's device-code lifetime.
When it happens
Trigger: User opened the verification URL but never entered the code or never approved; user is on a slow connection and the approval round-trip exceeded 10 minutes; the device_code lifetime is longer than this deadline and the user authorized just after timeout.
Common situations: Backgrounded app where the user forgot to finish; user got distracted mid-login; clock differences making the deadline trigger early.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ${response.status} ${response.statusText}: ${text}
- Device flow failed: ${raw.error}
- MiniMax OAuth timed out waiting for authorization.
- Qwen OAuth timed out waiting for authorization.
- MiniMax authorization failed: ${text}
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/77c05e7d34add5a8.
Report an issue: GitHub.