upstash/context7 · error · Error
Device token poll failed
Error message
Device token poll failed
What it means
Thrown in the default branch of pollDeviceToken()'s switch after a non-2xx, non-5xx response. Recognized OAuth device-code errors (authorization_pending, slow_down, access_denied, expired_token) return a structured status; any other error code falls through and throws err.error_description || err.error || 'Device token poll failed'.
Source
Thrown at packages/cli/src/utils/auth.ts:305
const err = (await response.json().catch(() => ({}))) as TokenErrorResponse;
return {
status: "transient",
errorMessage: err.error_description || err.error || `HTTP ${response.status}`,
};
}
const err = (await response.json().catch(() => ({}))) as TokenErrorResponse;
switch (err.error) {
case "authorization_pending":
return { status: "pending" };
case "slow_down":
return { status: "slow_down" };
case "access_denied":
return { status: "denied" };
case "expired_token":
return { status: "expired" };
default:
throw new Error(err.error_description || err.error || "Device token poll failed");
}
}
View on GitHub (pinned to ca15df0443)
Solutions
- Restart the device flow from scratch (new device_code) rather than resuming the poll.
- Update the CLI — newer versions may recognize the new error code.
- Inspect err.error in the thrown message; an unrecognized value usually means server/client contract drift.
- If err.error is undefined, the body was not JSON — check for an intercepting gateway and verify the auth host.
Example fix
// before — only the fallback string reaches the user
default:
throw new Error(err.error_description || err.error || "Device token poll failed");
// after — surface the raw error code so unrecognized codes are debuggable
const code = err.error || "unknown";
throw new Error(err.error_description || err.error || `Device token poll failed (error=${code})`); Defensive patterns
Strategy: validation
Validate before calling
// Only poll while the flow is plausibly live; stop after a max attempt budget.
const deadline = Date.now() + 5 * 60_000;
let attempt = 0;
while (Date.now() < deadline && attempt++ < 60) {
const r = await pollDeviceToken(baseUrl, clientId, deviceCode);
if (r.status === "approved") return r.tokens!;
if (r.status === "denied" || r.status === "expired") break;
await sleep((r.status === "slow_down" ? 10 : 5) * 1000);
}
throw new Error("Device authorization timed out or ended without approval"); Type guard
// Narrow the parsed token-error body.
function isTokenError(v: unknown): v is { error: string; error_description?: string } {
return typeof v === "object" && v !== null && typeof (v as any).error === "string";
} Try / catch
try {
await pollDeviceToken(baseUrl, clientId, deviceCode);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
// An unrecognized code usually means contract drift or a stale device_code;
// restart the flow rather than resuming.
if (/invalid_grant|invalid_client|unsupported_grant_type/.test(msg)) {
return restartDeviceFlow();
}
throw e;
} Prevention
- Start a brand-new device flow if polling fails — never reuse a device_code after an error.
- Cap polling by both attempt count and wall-clock deadline so unrecognized codes cannot loop forever.
- Keep the CLI current so newly introduced OAuth error codes are mapped to structured statuses.
When it happens
Trigger: OAuth server returns invalid_grant (device_code reused/expired unexpectedly), invalid_client, unsupported_grant_type, a new/proprietary error code, or a non-JSON body so err is {} and both err.error and err.error_description are undefined.
Common situations: Polling continued past token issuance causing a second poll with an already-consumed device_code; client_id rotated mid-flow; auth server upgraded and now emits an error code the CLI version does not know about; CDN/gateway replaced the body with HTML so the JSON parse silently yields {}.
Related errors
- Could not reach ${url}: ${detail}${code ? ` (${code})` : ""}
- ${fallback} (HTTP ${response.status} from ${response.url}):
- Failed to fetch user info
- no files
AI-assisted analysis of upstash/context7@ca15df0443 (2026-08-12).
Data as JSON: /api/errors/e03f00439b5c4e2f.
Report an issue: GitHub.