pbakaus/impeccable · error · Error
${body.error || res.statusText}\n${body.reason}\n${body.hint
Error message
${body.error || res.statusText}\n${body.reason}\n${body.hint}\n${failureLines}\n${body._instructions} What it means
Thrown by postReply() when the POST /poll reply gets a non-ok HTTP status. The error message is a join of all truthy parts from the JSON error body: body.error (or res.statusText), body.reason, body.hint, formatted failure lines (file:line message), and body._instructions. This is the server's structured rejection of a reply (e.g. a failed manual_edit_apply result).
Source
Thrown at plugin/skills/impeccable/scripts/live-poll.mjs:126
}
export function requiresAgentReply(event) {
return EVENT_TYPES_NEEDING_AGENT_REPLY.has(event?.type);
}
export async function postReply(base, token, reply) {
const res = await fetch(`${base}/poll`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(buildPollReplyPayload(token, reply)),
});
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);View on GitHub (pinned to d14711ae3d)
Solutions
- Read the joined parts: body.reason names the class (e.g. validation_failed, lease_expired), body.hint gives the next step, and failureLines list per-file failures to fix.
- For manual_edit_apply: re-open the listed source files, fix the failures (leftover markers, syntax), and re-Apply — the buffer re-stages.
- If the lease expired, the server dropped the event; wait for or trigger a new one rather than re-replying to the same id.
- If body._instructions is present it carries the authoritative next command with real ids — follow it.
- Confirm you are replying to the correct event id with the correct token (read from .impeccable/live/server.json).
Example fix
// before
await postReply(base, token, { id, type: 'done' });
// after
try {
await postReply(base, token, { id, type: 'done', data: resultJson });
} catch (err) {
console.error('Reply rejected:\\n' + err.message);
// err.message already contains reason, hint, failure lines, instructions
process.exit(1);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await postReply(base, token, reply);
} catch (err) {
// err.message already joins body.error, reason, hint, failure lines, _instructions.
const isLeaseExpired = /lease/i.test(err.message);
const isValidation = /validation|failed/i.test(err.message);
if (isValidation) { /* fix source per failure lines, re-Apply */ }
else if (isLeaseExpired) { /* wait for a new event, don't re-reply */ }
else { console.error('Reply rejected:', err.message); process.exit(1); }
} Prevention
- Reply within DEFAULT_EVENT_LEASE_MS (600s) — long agent runs must keep the lease alive or risk a rejection.
- For manual_edit_apply, ensure post-apply source is clean (no impeccable markers, valid JSON/syntax) before replying done.
- Parse err.message for reason/hint/_instructions rather than matching on status codes — the structured body carries the action.
When it happens
Trigger: Replying 'done' to a manual_edit_apply event but the server's validation of appliedEntryIds/files fails (body.failures populated); replying to an event the server no longer considers leased (expired lease -> 410); malformed reply payload the server rejects with 400; replying to an already-acknowledged event id. The server returns whatever status/reason it chose.
Common situations: Agent's applied source failed post-apply checks (leftover impeccable markers, invalid JSON, syntax error) so the server rejects the done reply; lease expired (DEFAULT_EVENT_LEASE_MS = 600s) while the agent was working; two pollers raced and one already replied; token mismatch causing a non-401 error.
Related errors
- Status failed: ${res.status} ${res.statusText}
- Poll failed: ${res.status} ${res.statusText}
- ${parts.join('\n')}
- Status failed: ${res.status} ${res.statusText}
- Poll failed: ${res.status} ${res.statusText}
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/271e30c522ebba92.
Report an issue: GitHub.