pbakaus/impeccable · error · Error

Status failed

Error message

Status failed: ${res.status} ${res.statusText}

What it means

fetchServerStatus(base, token) in skill/scripts/live-poll.mjs issues GET ${base}/status?token=${token} against the Impeccable live server. A 401 is mapped first to AUTH_FAILED ('The server token may have changed.'), so this throw means the server answered HTTP but with a different non-OK status — 404, 500, 502, 504, etc. — and the message embeds the numeric status plus statusText for diagnosis.

Solutions

  1. Confirm the live server is running and read the port it prints; rebuild base from it
  2. Reproduce directly: curl "${base}/status?token=${token}" and inspect the status code and body
  3. Check the live-server console for a stack trace when the status is 5xx
  4. Remove any reverse proxy from the URL or fix its timeouts if it fronts the live server
  5. If the status flips to 401/AUTH_FAILED, the server token changed — re-read the current token instead of retrying the old one
Defensive patterns

Strategy: retry

Validate before calling

async function serverReachable(base, token) {
  try {
    const res = await fetch(`${base}/status?token=${token}`);
    if (res.status === 401) return { ok: false, reason: 'AUTH_FAILED' };
    return { ok: res.ok, reason: res.ok ? null : `HTTP ${res.status}` };
  } catch (err) {
    return { ok: false, reason: err.cause?.code || err.message }; // ECONNREFUSED etc.
  }
}

const probe = await serverReachable(base, token);
if (!probe.ok) throw new Error(`live server not ready: ${probe.reason}`);

Try / catch

for (let attempt = 1; attempt <= 3; attempt++) {
  try {
    status = await fetchServerStatus(base, token);
    break;
  } catch (err) {
    if (err?.code === 'AUTH_FAILED') throw err; // token problem: retrying cannot help
    const transient = /^Status failed: 5/.test(err?.message || '');
    if (!transient || attempt === 3) throw err;
    await backoff(attempt);
  }
}

Prevention

When it happens

Trigger: base points at the wrong port or includes a wrong path so an intermediary 404s; the live server hit an internal error (500) while handling /status; a reverse proxy/gateway in front of the server returns 502/504; a stale base from a previous server instance whose port now hosts a different service.

Common situations: The live server was restarted on a new port while the polling script kept the old base; another dev server (Vite/Astro) now occupies the port; corporate proxies or SSL-terminating fronts intercepting localhost requests.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of pbakaus/impeccable@f88b2837a7 (2026-08-18). Data as JSON: /api/errors/241e50e7b1bfe963. Report an issue: GitHub.

Appendix: source

Thrown at skill/scripts/live-poll.mjs:138

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    const failureLines = Array.isArray(body.failures)
      ? body.failures.map((f) => `  ${f.file}${f.line != null ? `:${f.line}` : ''} ${f.message}`).join('\n')
      : null;
    const parts = [body.error || res.statusText, body.reason, body.hint, failureLines, body._instructions].filter(Boolean);
    throw new Error(parts.join('\n'));
  }
}

export async function fetchServerStatus(base, token) {
  const res = await fetch(`${base}/status?token=${token}`);
  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(`Status failed: ${res.status} ${res.statusText}`);
  }
  return res.json();
}

export function isEventPending(status, eventId) {
  return (status.pendingEvents || []).some((entry) => entry.id === eventId);
}

export async function waitForEventAck(base, token, eventId, {
  pollIntervalMs = 400,
  maxWaitMs = 600_000,
} = {}) {
  const deadline = Date.now() + maxWaitMs;
  while (Date.now() < deadline) {
    const status = await fetchServerStatus(base, token);
    if (!isEventPending(status, eventId)) return true;
    await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
  }

View on GitHub (pinned to f88b2837a7)