pbakaus/impeccable · error · Error
Poll failed: ${res.status} ${res.statusText}
Error message
Poll failed: ${res.status} ${res.statusText} What it means
Thrown by fetchNextEvent() when the GET /poll long-poll request returns a non-ok, non-401 status. Distinct from error 55 because /poll is the long-poll endpoint (with timeout/leaseMs/types query params). Like /status, 401 maps to AUTH_FAILED; any other status becomes 'Poll failed: <code> <reason>'. The function loops on 'timeout' responses, so this throw breaks the loop.
Source
Thrown at plugin/skills/impeccable/scripts/live-poll.mjs:193
: PER_REQUEST_TIMEOUT_MS;
const slice = Math.min(Math.max(remaining, 1000), perRequestTimeoutMs);
const query = new URLSearchParams({
token,
timeout: String(slice),
leaseMs: String(leaseMs),
});
const normalizedTypes = normalizePollTypes(resolveTypes ? await resolveTypes() : types);
if (normalizedTypes.length > 0) query.set('types', normalizedTypes.join(','));
const res = await fetch(`${base}/poll?${query}`, { signal });
if (res.status === 401) {
const err = new Error('Authentication failed. The server token may have changed.');
err.code = 'AUTH_FAILED';
throw err;
}
if (!res.ok) {
throw new Error(`Poll failed: ${res.status} ${res.statusText}`);
}
const next = await res.json();
if (next?.type === 'timeout') {
if (totalDeadline && Date.now() < totalDeadline) continue;
if (!totalDeadline) continue;
return next;
}
return next;
}
}
export async function augmentEventWithAcceptHandling(event, base, token) {
if (event.type !== 'accept' && event.type !== 'discard') return event;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const acceptScript = path.join(__dirname, 'live-accept.mjs');
const scriptArgs = buildAcceptScriptArgs(event);View on GitHub (pinned to d14711ae3d)
Solutions
- Restart the live server (stop via server.json pid, then `live.mjs`) and resume polling.
- Check server logs for the 5xx root cause — poll errors usually mirror an internal exception.
- Verify client and server are from the same skill version (the /poll contract includes token, timeout, leaseMs, types).
- If ECONNREFUSED, the server isn't running — that's surfaced separately by handlePollError, but a stale server.json can mislead.
Example fix
// before
const event = await fetchNextEvent(base, token, { types });
// after
try {
const event = await fetchNextEvent(base, token, { types });
} catch (err) {
if (err.code === 'AUTH_FAILED') { /* re-auth */ }
else if (/Poll failed:/.test(err.message)) {
console.error('Poll endpoint error, restart live session:', err.message);
}
throw err;
} Defensive patterns
Strategy: retry
Try / catch
async function safePoll(base, token, opts, retries = 1) {
for (let attempt = 0; ; attempt++) {
try {
return await fetchNextEvent(base, token, opts);
} catch (err) {
if (err.code === 'AUTH_FAILED') throw err;
if (attempt < retries && /Poll failed: 5\d\d/.test(err.message)) {
await new Promise((r) => setTimeout(r, 500));
continue;
}
throw err;
}
}
} Prevention
- Distinguish AUTH_FAILED (restart auth) from transient 5xx (retry) from ECONNREFUSED (server down — restart live.mjs).
- Keep client and server on the same skill build so /poll query params (token, timeout, leaseMs, types) match.
- fetchNextEvent loops internally on 'timeout' responses; only non-ok throws break the loop, so surface them.
When it happens
Trigger: Server returns 500 due to an internal error mid-lease; 404 if the /poll route is missing (version skew); 429 if the server rate-limits; 503 during shutdown. The poll uses a perRequestTimeoutMs capped at 270s (PER_REQUEST_TIMEOUT_MS) to stay under Node fetch's 300s header timeout.
Common situations: Server crashed under load; polling after the server was stopped but before the port was released to another process; version mismatch where the client sends query params the server doesn't understand; lease corruption causing the server to error on event delivery.
Related errors
- Poll failed: ${res.status} ${res.statusText}
- ${body.error || res.statusText}\n${body.reason}\n${body.hint
- Status failed: ${res.status} ${res.statusText}
- ${parts.join('\n')}
- Status failed: ${res.status} ${res.statusText}
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/efd97c4f9f5be968.
Report an issue: GitHub.