{"record":{"id":"e03f00439b5c4e2f","repo":"upstash/context7","slug":"device-token-poll-failed","errorCode":null,"errorMessage":"Device token poll failed","messagePattern":"Device token poll failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/auth.ts","lineNumber":305,"sourceCode":"    const err = (await response.json().catch(() => ({}))) as TokenErrorResponse;\n    return {\n      status: \"transient\",\n      errorMessage: err.error_description || err.error || `HTTP ${response.status}`,\n    };\n  }\n\n  const err = (await response.json().catch(() => ({}))) as TokenErrorResponse;\n  switch (err.error) {\n    case \"authorization_pending\":\n      return { status: \"pending\" };\n    case \"slow_down\":\n      return { status: \"slow_down\" };\n    case \"access_denied\":\n      return { status: \"denied\" };\n    case \"expired_token\":\n      return { status: \"expired\" };\n    default:\n      throw new Error(err.error_description || err.error || \"Device token poll failed\");\n  }\n}\n","sourceCodeStart":287,"sourceCodeEnd":308,"githubUrl":"https://github.com/upstash/context7/blob/ca15df0443ee770506fc4eb270d1efc71d483933/packages/cli/src/utils/auth.ts#L287-L308","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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 {}.","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."],"exampleFix":"// before — only the fallback string reaches the user\ndefault:\n  throw new Error(err.error_description || err.error || \"Device token poll failed\");\n\n// after — surface the raw error code so unrecognized codes are debuggable\nconst code = err.error || \"unknown\";\nthrow new Error(err.error_description || err.error || `Device token poll failed (error=${code})`);","handlingStrategy":"validation","validationCode":"// Only poll while the flow is plausibly live; stop after a max attempt budget.\nconst deadline = Date.now() + 5 * 60_000;\nlet attempt = 0;\nwhile (Date.now() < deadline && attempt++ < 60) {\n  const r = await pollDeviceToken(baseUrl, clientId, deviceCode);\n  if (r.status === \"approved\") return r.tokens!;\n  if (r.status === \"denied\" || r.status === \"expired\") break;\n  await sleep((r.status === \"slow_down\" ? 10 : 5) * 1000);\n}\nthrow new Error(\"Device authorization timed out or ended without approval\");","typeGuard":"// Narrow the parsed token-error body.\nfunction isTokenError(v: unknown): v is { error: string; error_description?: string } {\n  return typeof v === \"object\" && v !== null && typeof (v as any).error === \"string\";\n}","tryCatchPattern":"try {\n  await pollDeviceToken(baseUrl, clientId, deviceCode);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  // An unrecognized code usually means contract drift or a stale device_code;\n  // restart the flow rather than resuming.\n  if (/invalid_grant|invalid_client|unsupported_grant_type/.test(msg)) {\n    return restartDeviceFlow();\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["oauth","device-flow","cli"],"backgroundTag":null,"analyzedSha":"ca15df0443ee770506fc4eb270d1efc71d483933","analyzedAt":"2026-08-12T13:31:48.440Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}