JuliusBrussee/caveman · error · Error
device login polling failed: ${error instanceof Error ? erro
Error message
device login polling failed: ${error instanceof Error ? error.message : String(error)} What it means
During token polling, if the poll request itself throws (network failure, malformed body from response.json()) and the code deadline has passed, the library wraps the underlying error in this message instead of a plain timeout error. It distinguishes 'polling broke and time ran out' from orderly expiry.
Source
Thrown at packages/device-auth/src/index.ts:134
let retryAfterMs = 0;
try {
const response = await fetcher(`${baseURL}/api/v1/auth/device/token`, {
method: "POST",
headers: { "content-type": "application/json", "x-cave-client": options.client },
body: JSON.stringify({ device_code: code.device_code }),
signal: requestSignal(options.signal, 5000),
});
status = response.status;
const retryAfter = response.headers.get("retry-after");
if (retryAfter !== null) {
const seconds = Number(retryAfter);
if (Number.isFinite(seconds) && seconds >= 0) retryAfterMs = seconds * 1000;
}
payload = await response.json() as Record<string, unknown>;
} catch (error) {
if (options.signal?.aborted) throw options.signal.reason;
if (Date.now() >= deadline) {
throw new Error(`device login polling failed: ${error instanceof Error ? error.message : String(error)}`);
}
await wait(Math.max(intervalMs, retryAfterMs, 200));
continue;
}
if (status === 429) {
await wait(Math.max(intervalMs, retryAfterMs, 200));
continue;
}
const accessToken = typeof payload.access_token === "string" ? payload.access_token : "";
if (accessToken !== "") {
const credentials = { ...payload, access_token: accessToken } as DeviceCredentials;
let acknowledged = false;
return {
code: structuredClone(code),
credentials: structuredClone(credentials),
async acknowledge() {
if (acknowledged) return;
await acknowledge({View on GitHub (pinned to df2ccd85c9)
Solutions
- Restart the device flow to get a fresh code — the original code has expired, so retrying cannot succeed.
- Fix the underlying network issue shown in the inner error (VPN, DNS, proxy) before retrying.
- Shorten approval time: open verification_uri promptly so polling completes before expiry.
- Check why the token endpoint returned unparseable JSON (proxy interference, server error page) via `curl -i` to the token endpoint.
Example fix
// before (starting the flow, then idling until the code expires)
await runCavemanDeviceFlow({ ... }); // user approves 20 minutes later
// after
await runCavemanDeviceFlow({ ...onCode: async (code) => { await openBrowser(code.verification_uri); } }); // approve promptly, well before expires_in elapses Defensive patterns
Strategy: try-catch
Try / catch
try {
await runCavemanDeviceFlow(options);
} catch (e) {
if (e instanceof Error && e.message.includes("device login polling failed")) {
console.error("Network error while polling after code expiry — restart the device flow on a stable connection");
} else throw e;
} Prevention
- Approve the device code promptly; don't let it sit until expiry.
- Run device flows on stable networks (avoid suspending the machine mid-flow).
- Ensure the token endpoint returns JSON, not HTML error pages (check proxies).
- Restart the flow after any network interruption — expired codes cannot be resumed.
When it happens
Trigger: A poll to the token endpoint throws inside the try block (fetch network error, invalid JSON, aborted-connection issues) while Date.now() >= deadline — i.e. the failure happens on or after the device-code expiry.
Common situations: User waits until the code nearly expires, then the network drops; flaky Wi-Fi/VPN during long polls; token endpoint returning non-JSON (HTML error page) on the final poll; sleeping laptop suspending the connection until after expiry.
Related errors
- binary download failed: ${error.message}
- device credential delivery acknowledgement failed (${lastErr
- device authorization failed: HTTP ${codeResponse.status}
- device login failed: ${errorCode}
- AbortError
AI-assisted analysis of JuliusBrussee/caveman@df2ccd85c9 (2026-08-31).
Data as JSON: /api/errors/fbd49cba268080d7.
Report an issue: GitHub.