pbakaus/impeccable · error
steer_done_requires_file_or_message
steer_done_requires_file_or_message
Error message
steer_done_requires_file_or_message
What it means
When the event being acknowledged is a steer request and the reply type is steer_done, the server requires evidence of work: either msg.file (the source file the agent wrote) or a non-empty trimmed message string explaining an intentional no-op. Missing both yields HTTP 400 with error steer_done_requires_file_or_message and a hint repeating exactly those two options.
Source
Thrown at skill/scripts/live-server.mjs:1333
if (msg.type === 'retry') {
const releasedEvent = releasePendingEvent(msg.id, sourceEventType);
if (!releasedEvent) {
res.writeHead(msg.id ? 404 : 400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: msg.id ? 'unknown_poll_retry_id' : 'missing_poll_retry_id',
id: msg.id,
}));
return;
}
flushPendingPolls();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, released: true }));
return;
}
const pendingEventBeforeAck = findPendingEventById(msg.id, sourceEventType);
if (pendingEventBeforeAck?.type === 'steer' && msg.type === 'steer_done'
&& !msg.file && !(typeof msg.message === 'string' && msg.message.trim())) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'steer_done_requires_file_or_message',
hint: 'Reply with --file after writing source, or include a message explaining an intentional no-op.',
}));
return;
}
const acknowledgedEvent = acknowledgePendingEvent(msg.id, sourceEventType);
let skipJournalReply = false;
let existingSession = null;
if (!acknowledgedEvent && state.sessionStore && msg.id) {
try {
existingSession = state.sessionStore.getSnapshot(msg.id, { includeCompleted: true });
if (!existingSession?.updatedAt) existingSession = null;
skipJournalReply = existingSession?.phase === 'completed' || existingSession?.phase === 'discarded';
} catch { /* fall through and record the reply normally */ }
}
if (!acknowledgedEvent && !existingSession) {
recordManualEditActivity('manual_edit_poll_reply_unknown', {View on GitHub (pinned to f88b2837a7)
Solutions
- If source was written: resend with the file attached, e.g. --reply <id> done --file src/Button.svelte
- If intentional no-op: resend with a non-empty --message 'No change needed: X already does Y'
- Check the pending event type first — this rule only fires for steer events acknowledged as steer_done
Example fix
// before live-poll.mjs --reply evt_9 done // after live-poll.mjs --reply evt_9 done --file src/hero/Hero.svelte // or: live-poll.mjs --reply evt_9 done --message 'No-op: requested variant already the default'
Defensive patterns
Strategy: validation
Validate before calling
// Before sending steer_done, ensure evidence of work is attached
const ok = Boolean(msg.file) || (typeof msg.message === 'string' && msg.message.trim().length > 0);
if (!ok) throw new Error('attach --file or a non-empty --message before steer_done'); Type guard
function hasFileOrMessage(m) {
return Boolean(m?.file) || (typeof m?.message === 'string' && m.message.trim().length > 0);
} Prevention
- Make 'no-op needs a reason' a habit: every steer_done carries either the file or one sentence
- Trim and assert message length > 0, whitespace does not count
- Check the pending event type before choosing the reply shape
When it happens
Trigger: Running live-poll.mjs --reply <id> done for a steer event without --file and without --message; passing --message ' ' (whitespace-only); passing the file in the wrong field name (e.g. filePath instead of file).
Common situations: The agent concludes the steer needs no code change but forgets to explain the no-op; the agent edits a file but replies before attaching --file; shell quoting eats an empty-but-intended message.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- invalid_manual_apply_result
- Status failed: ${res.status} ${res.statusText}
- Poll failed: ${res.status} ${res.statusText}
- Live browser script part missing: ${part.name} (${part.path}
- Not found
AI-assisted analysis of pbakaus/impeccable@f88b2837a7 (2026-08-18).
Data as JSON: /api/errors/b700815d51a9e6b0.
Report an issue: GitHub.