coleam00/Archon · info · DeviceFlowError

aborted

aborted

Error message

Device flow polling was aborted

What it means

pollDeviceFlow loops until the user authorizes the device. Before each sleep it checks opts.signal; if the caller aborted the poll, it throws DeviceFlowError with code 'aborted' so callers can distinguish intentional cancellation from auth failure.

Solutions

  1. Treat code 'aborted' as an expected cancellation, not an auth failure — return to the login prompt
  2. Only abort when you actually intend to cancel; pass signal: undefined otherwise
  3. Wrap the poll in try/catch that special-cases aborted and restarts the device flow on demand

Example fix

// before
const token = await pollDeviceFlow(clientId, data, 5, { signal });
// after
try {
  const token = await pollDeviceFlow(clientId, data, 5, { signal });
} catch (e) {
  if (e instanceof DeviceFlowError && e.code === 'aborted') return null;
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try { return await pollDeviceFlow(clientId, deviceCode, interval, { signal }); } catch (e) { if (e instanceof DeviceFlowError && e.code === 'aborted') return null; throw e; }

Prevention

When it happens

Trigger: Calling pollDeviceFlow with an AbortSignal and aborting it (e.g. user cancels login, UI timeout, shutdown) while polling is still pending.

Common situations: User closes the browser before entering the code; CLI cancelled with Ctrl-C mapped to an AbortController; server shutting down mid-login; application-level login timeout.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/68fe915029d63735. Report an issue: GitHub.

Appendix: source

Thrown at packages/core/src/github-auth/device-flow.ts:117

const defaultSleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));

/**
 * Step 2: poll until the user authorizes. Handles `authorization_pending`
 * (keep waiting) and `slow_down` (back off using the server-supplied interval).
 * Any other `error` is terminal and thrown as a DeviceFlowError.
 */
export async function pollDeviceFlow(
  clientId: string,
  deviceCode: string,
  intervalSeconds: number,
  opts: PollOptions = {}
): Promise<DeviceAccessToken> {
  const sleep = opts.sleep ?? defaultSleep;
  let interval = Math.max(1, intervalSeconds);
  for (;;) {
    if (opts.signal?.aborted) {
      throw new DeviceFlowError('aborted', 'Device flow polling was aborted');
    }
    await sleep(interval * 1000);
    const result = await pollDeviceFlowOnce(clientId, deviceCode);
    if (result.status === 'authorized') return result.token;
    if (result.status === 'pending') continue;
    if (result.status === 'slow_down') {
      // Honor the server's new interval, else keep the current. Floor at 1s so a
      // malformed `interval: 0` can't turn this into a busy loop.
      interval = Math.max(1, result.interval);
      continue;
    }
    throw new DeviceFlowError(result.code);
  }
}

/** Result of a single (non-blocking) device-flow poll. */
export type PollOnceResult =
  | { status: 'pending' }

View on GitHub (pinned to 0773b97458)