{"record":{"id":"4d59091a59c0541c","repo":"JuliusBrussee/caveman","slug":"device-login-timed-out-before-approval","errorCode":null,"errorMessage":"device login timed out before approval","messagePattern":"device login timed out before approval","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":9793,"sourceCode":"      } else if (gateway) {\n        console.error(`  ${mark(\"ok\")} connected; wrap routes through ${gateway}`);\n      }\n      console.error(SYNC_DISCLOSURE);\n      print({ authenticated: true, baseURL, gateway_url: gateway || null, organization_id: organizationId ?? null, token_store: tokenStore });\n      // The funnel bridge: pull the spans the local proxy already measured into\n      // the dashboard, once, right now (always labeled inferred; best-effort).\n      await syncAfterLogin();\n      return;\n    }\n    const errorCode = typeof tok.error === \"string\" ? tok.error : \"\";\n    if (errorCode === \"slow_down\") {\n      intervalMs = nextDevicePollIntervalMs(intervalMs, errorCode);\n    } else if (errorCode && errorCode !== \"authorization_pending\") {\n      throw new Error(`device login failed: ${errorCode}`);\n    }\n    await sleep(Math.max(intervalMs, 200));\n  }\n  throw new Error(\"device login timed out before approval\");\n}\n\nasync function logout() {\n\tconst cfg = await config();\n\tconst externalToken = Boolean(process.env.CAVE_TOKEN);\n\tif (cfg.token && (!cfg.logoutPendingLocalCleanup || externalToken)) {\n\t  if (cfg.projectId && cfg.gatewayKeyId) {\n\t    let response: Response;\n\t    try {\n\t      response = await fetch(`${cfg.baseURL}/api/v1/projects/${encodeURIComponent(cfg.projectId)}/keys/${encodeURIComponent(cfg.gatewayKeyId)}/revoke`, {\n\t        method: \"POST\",\n\t        headers: { authorization: `Bearer ${cfg.token}`, \"content-type\": \"application/json\", \"x-cave-csrf\": \"cli\" },\n\t        body: \"{}\",\n\t        signal: AbortSignal.timeout(5000),\n\t      });\n\t    } catch {\n\t      throw new Error(\"caveman: remote gateway key revocation was unavailable; credentials kept — retry `caveman logout`\");\n\t    }","sourceCodeStart":9775,"sourceCodeEnd":9811,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L9775-L9811","documentation":"Thrown by the CLI device-login (OAuth device flow) loop in `caveman login`. The loop polls the authorization server for approval and exits with this error when the retry budget is exhausted before the user approves the sign-in. It indicates the device code expired or the user never completed browser approval in time.","triggerScenarios":"Running `caveman login` and never visiting the verification URL, visiting it after the device code expired, or the poll loop exceeding its max attempts while `authorization_pending` persists.","commonSituations":"Developer runs login, gets distracted and doesn't open the browser link; slow corporate SSO approval; stale browser session where the approve button silently fails; polling interval backoff (nextDevicePollIntervalMs) exceeding the server's device-code lifetime.","solutions":["Re-run `caveman login` and approve promptly in the browser when prompted.","Open the verification URL in the default browser immediately (copy the code before it expires).","Check for slow SSO/VPN causing delayed approval; approve on a faster network.","If it repeatedly times out, verify the gateway/auth server is reachable and not rate-limiting (slow_down responses inflate intervalMs)."],"exampleFix":"// before: approve hours later after code expiry\ncaveman login  # ignore prompt, approve 30 min later\n// after\ncaveman login  # approve in browser immediately when the code is shown","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await login();\n} catch (e) {\n  if (e instanceof Error && e.message === \"device login timed out before approval\") {\n    console.error(\"Approval not completed in time — re-running login; approve in the browser promptly.\");\n    return login(); // one bounded retry\n  }\n  throw e;\n}","preventionTips":["Approve the device-flow prompt in the browser as soon as the code is displayed.","Copy the verification URL/code immediately; don't let the device code expire.","Run login on a network where the SSO provider is reachable and fast."],"tags":["auth","device-flow","timeout","cli"],"backgroundTag":"request-timeout","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}