JuliusBrussee/caveman · error · Error

device login failed: ${errorCode}

Error message

device login failed: ${errorCode}

What it means

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.

Source

Thrown at packages/cli/src/index.ts:9426

      // effort: login never fails for seats or a down entitlement service.
      await fetchAndStoreWrapEntitlement(baseURL, credentials.access_token);
      if (gateway && wrapMode(gateway) === "managed") {
        console.error(`  ${mark("ok")} wrap now routes through the managed gateway (${gateway}) — governed reporting; verified stays zero without qualifying provider evidence`);
      } else if (gateway) {
        console.error(`  ${mark("ok")} connected; wrap routes through ${gateway}`);
      }
      console.error(SYNC_DISCLOSURE);
      print({ authenticated: true, baseURL, gateway_url: gateway || null, organization_id: organizationId ?? null, token_store: tokenStore });
      // The funnel bridge: pull the spans the local proxy already measured into
      // the dashboard, once, right now (always labeled inferred; best-effort).
      await syncAfterLogin();
      return;
    }
    const errorCode = typeof tok.error === "string" ? tok.error : "";
    if (errorCode === "slow_down") {
      intervalMs = nextDevicePollIntervalMs(intervalMs, errorCode);
    } else if (errorCode && errorCode !== "authorization_pending") {
      throw new Error(`device login failed: ${errorCode}`);
    }
    await sleep(Math.max(intervalMs, 200));
  }
  throw new Error("device login timed out before approval");
}

async function logout() {
	const cfg = await config();
	const externalToken = Boolean(process.env.CAVE_TOKEN);
	if (cfg.token && (!cfg.logoutPendingLocalCleanup || externalToken)) {
	  if (cfg.projectId && cfg.gatewayKeyId) {
	    let response: Response;
	    try {
	      response = await fetch(`${cfg.baseURL}/api/v1/projects/${encodeURIComponent(cfg.projectId)}/keys/${encodeURIComponent(cfg.gatewayKeyId)}/revoke`, {
	        method: "POST",
	        headers: { authorization: `Bearer ${cfg.token}`, "content-type": "application/json", "x-cave-csrf": "cli" },
	        body: "{}",
	        signal: AbortSignal.timeout(5000),

View on GitHub (pinned to 5184b3d11a)

Solutions

  1. Read errorCode: expired_token → restart login promptly; access_denied → approval was refused
  2. Restart `caveman login` for a fresh device code
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at packages/cli/src/index.ts:8581 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18). Data as JSON: /api/errors/6344f5febc395881. Report an issue: GitHub.