{"record":{"id":"06229a6bb58b97df","repo":"JuliusBrussee/caveman","slug":"device-authorization-failed-http-coderesponse-s","errorCode":null,"errorMessage":"device authorization failed: HTTP ${codeResponse.status}","messagePattern":"device authorization failed: HTTP (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/device-auth/src/index.ts","lineNumber":102,"sourceCode":"\nexport async function runCavemanDeviceFlow(options: {\n  baseURL: string;\n  client: string;\n  fetch?: typeof globalThis.fetch;\n  signal?: AbortSignal;\n  sleep?: (ms: number) => Promise<void>;\n  onCode?: (code: DeviceCode) => void | Promise<void>;\n}): Promise<DeviceGrant> {\n  const fetcher = options.fetch ?? globalThis.fetch;\n  const wait = options.sleep ?? defaultSleep;\n  const baseURL = options.baseURL.replace(/\\/$/, \"\");\n  const codeResponse = await fetcher(`${baseURL}/api/v1/auth/device/code`, {\n    method: \"POST\",\n    headers: { \"content-type\": \"application/json\", \"x-cave-client\": options.client },\n    body: \"{}\",\n    signal: requestSignal(options.signal, 5000),\n  });\n  if (!codeResponse.ok) throw new Error(`device authorization failed: HTTP ${codeResponse.status}`);\n  const rawCode = await codeResponse.json().catch(() => null) as Partial<DeviceCode> | null;\n  if (rawCode === null || typeof rawCode.device_code !== \"string\" || rawCode.device_code === \"\" ||\n    typeof rawCode.user_code !== \"string\" || typeof rawCode.verification_uri !== \"string\" ||\n    typeof rawCode.expires_in !== \"number\" || !Number.isFinite(rawCode.expires_in) || rawCode.expires_in <= 0) {\n    throw new Error(`device authorization failed: ${JSON.stringify(rawCode)}`);\n  }\n  const code = rawCode as DeviceCode;\n  await options.onCode?.(structuredClone(code));\n  let intervalMs = Math.max(0, Number(code.interval ?? 5)) * 1000;\n  const deadline = Date.now() + code.expires_in * 1000;\n  while (Date.now() < deadline) {\n    let payload: Record<string, unknown>;\n    let status = 0;\n    let retryAfterMs = 0;\n    try {\n      const response = await fetcher(`${baseURL}/api/v1/auth/device/token`, {\n        method: \"POST\",\n        headers: { \"content-type\": \"application/json\", \"x-cave-client\": options.client },","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/df2ccd85c94ec3c8289cb62ac020d241ccfb0c60/packages/device-auth/src/index.ts#L84-L120","documentation":"runCavemanDeviceFlow POSTs to /api/v1/auth/device/code with a 5-second timeout to start the device authorization grant. A non-2xx response (bad gateway, auth server error, rate limit, wrong path) aborts immediately with the HTTP status embedded in this error.","triggerScenarios":"The POST /api/v1/auth/device/code request returns any !ok status — e.g. 404 (wrong baseURL/path), 500 (server error), 429 (rate limited), 502/503 (gateway down).","commonSituations":"baseURL pointing to a stale or wrong environment after an API version change; auth service outage; corporate proxy returning 403/502 for the endpoint; aggressive client causing 429s.","solutions":["Print/verify the status in the message and hit the endpoint manually: `curl -i -X POST ${baseURL}/api/v1/auth/device/code` to see the real error.","Fix baseURL to the correct environment/API version.","If 429, wait and retry with backoff.","Check proxy/firewall rules if an intermediary (403/502/503) is generating the status.","Retry during a confirmed server outage; check the provider's status page."],"exampleFix":"// before\nawait runCavemanDeviceFlow({ baseURL: \"https://api.example.com/v2\", ... }); // v2 has no /auth/device/code\n// after\nawait runCavemanDeviceFlow({ baseURL: \"https://api.example.com/v1\", ... }); // correct API version","handlingStrategy":"retry","validationCode":"const res = await fetch(`${baseURL}/api/v1/auth/device/code`, { method: \"HEAD\" }).catch(() => null);\nif (!res || !res.ok) throw new Error(`device code endpoint not healthy (baseURL=${baseURL})`);","typeGuard":null,"tryCatchPattern":"try {\n  await runCavemanDeviceFlow(options);\n} catch (e) {\n  if (e instanceof Error && /device authorization failed: HTTP (429|5\\d\\d)/.test(e.message)) {\n    await sleep(10000);           // transient server/rate-limit issue\n    await runCavemanDeviceFlow(options);\n  } else throw e;\n}","preventionTips":["Verify baseURL and API version before starting the flow.","Add startup reachability checks for the auth endpoint.","Back off on 429s instead of hammering the endpoint.","Watch provider status pages during auth-service incidents."],"tags":["device-auth","http","api-error","network"],"backgroundTag":"device-code-request-failed","analyzedSha":"df2ccd85c94ec3c8289cb62ac020d241ccfb0c60","analyzedAt":"2026-08-31T22:10:17.934Z","contentChangedAt":"2026-08-31T22:10:17.934Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}