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

  1. If source was written: resend with the file attached, e.g. --reply <id> done --file src/Button.svelte
  2. If intentional no-op: resend with a non-empty --message 'No change needed: X already does Y'
  3. 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

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


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