upstash/context7 · error · Error

err.error_description || err.error || "Device token poll fai

Error message

err.error_description || err.error || "Device token poll failed"

What it means

`pollDeviceToken` maps RFC 8628 device-flow error codes to poll statuses (authorization_pending, slow_down, access_denied, expired_token) and treats 5xx as transient above. This throw is the default branch: the token endpoint returned a non-ok, non-5xx response whose `error` code the CLI does not recognize, so it throws `error_description || error || "Device token poll failed"`.

Source

Thrown at packages/cli/src/utils/auth.ts:305

    const err = (await response.json().catch(() => ({}))) as TokenErrorResponse;
    return {
      status: "transient",
      errorMessage: err.error_description || err.error || `HTTP ${response.status}`,
    };
  }

  const err = (await response.json().catch(() => ({}))) as TokenErrorResponse;
  switch (err.error) {
    case "authorization_pending":
      return { status: "pending" };
    case "slow_down":
      return { status: "slow_down" };
    case "access_denied":
      return { status: "denied" };
    case "expired_token":
      return { status: "expired" };
    default:
      throw new Error(err.error_description || err.error || "Device token poll failed");
  }
}

View on GitHub (pinned to 5284672feb)

Solutions

  1. Restart the login flow to get a fresh device_code and user_code
  2. Make sure the whole flow runs against one baseUrl (no mid-flow env changes)
  3. Update the CLI so it knows the latest error codes
  4. Check the message body — it carries the server's own error_description
Defensive patterns

Strategy: try-catch

Validate before calling

// Only poll while the device code is still live
const deadline = Date.now() + response.expires_in * 1000;
if (Date.now() >= deadline) throw new Error('device_code already expired — restart login');

Type guard

function isKnownDeviceFlowError(e: string): e is 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token' {
  return ['authorization_pending', 'slow_down', 'access_denied', 'expired_token'].includes(e);
}

Try / catch

try {
  const result = await pollDeviceToken(baseUrl, clientId, deviceCode);
  if (result.status === 'transient') continue; // keep polling
} catch (error) {
  // Unknown error code: terminal — restart the whole device flow, do not retry the poll
  throw new Error(`device flow aborted: ${error instanceof Error ? error.message : error}`);
}

Prevention

When it happens

Trigger: Polling /api/oauth/device/token with a device_code that was issued for a different client_id or baseUrl (`invalid_grant`); `invalid_client` from a stale CLI_CLIENT_ID; the server introducing a new error code the CLI build predates.

Common situations: Device code from a different CLI version or base URL; clock skew or code typed into a different environment; server-side changes to the device flow after an outage.

Related errors


AI-assisted analysis of upstash/context7@5284672feb (2026-08-18). Data as JSON: /api/errors/29afd4eaa1364c05. Report an issue: GitHub.