decolua/9router · error · Error
data.errorDescription || data.error
Error message
data.errorDescription || data.error
What it means
During the device-code polling loop in OAuthModal, when the poll response carries error === "expired_token" or "access_denied", the modal throws an Error preferring the provider-supplied `errorDescription`, falling back to the bare error code. This ends polling with the error step. `slow_down` and `authorization_pending` are handled separately (backoff / keep polling) and do not reach this throw.
Source
Thrown at src/shared/components/OAuthModal.js:163
try {
const res = await fetch(`/api/oauth/${provider}/poll`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ deviceCode, codeVerifier, extraData }),
});
const data = await res.json();
if (data.success) {
pollingAbortRef.current = true; // Stop polling immediately
setStep("success");
setPolling(false);
onSuccess?.();
return;
}
if (data.error === "expired_token" || data.error === "access_denied") {
throw new Error(data.errorDescription || data.error);
}
if (data.error === "slow_down") {
interval = Math.min(interval + 5, 30);
}
} catch (err) {
setError(err.message);
setStep("error");
setPolling(false);
return;
}
}
setError("Authorization timeout");
setStep("error");
setPolling(false);
}, [provider, onSuccess]);
View on GitHub (pinned to 90b52e06ff)
Solutions
- Restart the device flow to get a new device/user code and approve it promptly.
- If access_denied, re-run the flow and approve the consent screen (check with your admin if SSO policy blocks it).
- Read the errorDescription in the thrown message — it states whether the code expired or consent was denied.
- For repeated expired_token, reduce friction: have the auth page open and ready before starting, and don't pause/hibernate the machine mid-flow.
Example fix
// before
if (data.error === "expired_token" || data.error === "access_denied") {
throw new Error(data.errorDescription || data.error);
}
// after
if (data.error === "expired_token" || data.error === "access_denied") {
const reason = data.error === "expired_token"
? "Device code expired — restart the sign-in and approve within the time limit"
: "Authorization was denied — restart the sign-in and approve the consent request";
throw new Error(data.errorDescription || reason);
} Defensive patterns
Strategy: retry
Validate before calling
// only treat terminal errors as fatal; keep polling pending/slow_down
const TERMINAL = ["expired_token", "access_denied"];
if (TERMINAL.includes(data.error)) {
// stop polling and prompt a fresh device-code request
setPolling(false);
setError(data.errorDescription || data.error);
return;
} Try / catch
try {
await pollDeviceToken();
} catch (err) {
if (err.message.includes("expired")) {
// auto-restart the device flow once
await startDeviceFlow();
} else {
setError(err.message);
}
} Prevention
- Approve the device-code page promptly; codes expire in minutes.
- Implement automatic restart of the device flow on expired_token.
- Respect slow_down backoff so the provider doesn't kill the poll window.
- Show a visible countdown so users know the approval deadline.
When it happens
Trigger: The device-flow poll endpoint returns { error: "expired_token" } — the device code outlived its validity because the user took too long to authorize — or { error: "access_denied" } — the user explicitly denied the consent request at the provider.
Common situations: User leaves the device-authorization page open but doesn't approve before the code expires (typically 5–15 min); user clicks 'Deny'/'Cancel' on the consent screen; interval backoff mishandled causing provider-side expiry; workplace SSO policy auto-denies the request.
Related errors
- `CodeBuddy state request failed: ${await response.text()}`
- `CodeBuddy state error: ${data.msg || "missing state/authUrl
- `CodeBuddy Intl state request failed: ${await response.text(
- `CodeBuddy Intl state error: ${data.msg || "missing state/au
- `Device code request failed: ${error}`
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/0679245971e41e0f.
Report an issue: GitHub.