{"record":{"id":"6344f5febc395881","repo":"JuliusBrussee/caveman","slug":"device-login-failed-errorcode","errorCode":null,"errorMessage":"device login failed: ${errorCode}","messagePattern":"device login failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":9426,"sourceCode":"      // effort: login never fails for seats or a down entitlement service.\n      await fetchAndStoreWrapEntitlement(baseURL, credentials.access_token);\n      if (gateway && wrapMode(gateway) === \"managed\") {\n        console.error(`  ${mark(\"ok\")} wrap now routes through the managed gateway (${gateway}) — governed reporting; verified stays zero without qualifying provider evidence`);\n      } 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),","sourceCodeStart":9408,"sourceCodeEnd":9444,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L9408-L9444","documentation":"The device-login token poll returned a terminal error code from RFC 8628 (e.g. access_denied or expired_token) rather than the retryable authorization_pending/slow_down. The flow has failed and cannot succeed by continued polling; the server's error code is reported.","triggerScenarios":"Thrown at packages/cli/src/index.ts:8581 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Read errorCode: expired_token → restart login promptly; access_denied → approval was refused","Restart `caveman login` for a fresh device code"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"5184b3d11ac6a1acb7d44b9bfaa31698157cff97","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}