Hmbown/CodeWhale · error · Error

Missing Engine .

Error message

Missing Engine ${field}.

What it means

Inside observe, the id(field) helper extracts a required string identifier (tool_call_id, tool_name, or id) for events that need one. It throws `Missing Engine ${field}.` when the field is absent, not a string, or an empty string. The template makes the exact field name part of the message, e.g. 'Missing Engine tool_call_id.'

Solutions

  1. Always include the required string fields: tool_call_id and tool_name for tool events, id for agent events
  2. Stringify numeric ids at the bridge boundary (String(id))
  3. Skip or repair events with empty identifiers before feeding telemetry
  4. Check which field name appears in the message and fix the producer for that specific field

Example fix

// before
telemetry.observe({ event: 'tool_call_started', tool_name: 'shell' }, at); // no tool_call_id
// after
telemetry.observe({ event: 'tool_call_started', tool_call_id: String(callId), tool_name: 'shell' }, at);
Defensive patterns

Strategy: validation

Validate before calling

function requireString(e, field) {
  const s = e[field];
  if (typeof s !== 'string' || !s) throw new Error(`Missing Engine ${field}.`);
  return s;
}
// call before observe for events needing ids
if (e.event.startsWith('tool_call_')) requireString(e, 'tool_call_id');

Try / catch

try {
  telemetry.observe(meta, at);
} catch (err) {
  const m = err.message.match(/^Missing Engine (\w+)\.$/);
  if (m) { console.warn(`engine event missing ${m[1]}`, meta); return; }
  throw err;
}

Prevention

When it happens

Trigger: Sending tool_call_started/tool_call_complete without tool_call_id or tool_name; sending agent_spawned/agent_progress/agent_complete without id; passing an empty-string identifier (id('') fails the !s check); passing a numeric id where a string is required.

Common situations: A protocol producer omitting the id on synthetic heartbeat/complete events; an id of 0 or a number from a JSON bridge not being stringified; empty tool names from unnamed tool wrappers.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/e967ff56304c6848. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/pet_watch/pet-native.js:1744

    }
    observe(value, at) {
        if (!Number.isFinite(at) || at < this.lastTime || at > pet_sim_js_1.PET_MAX_SECONDS * 1000)
            throw new Error('Invalid Engine pet clock.');
        if (!value || typeof value !== 'object' || Array.isArray(value))
            throw new Error('Invalid Engine pet metadata.');
        const e = value;
        const allowed = ['event', 'index', 'channel', 'tool_call_id', 'tool_name', 'id', 'worker_status', 'failed'];
        if (Object.keys(e).some(k => !allowed.includes(k)) || typeof e.event !== 'string'
            || Object.values(e).some(v => typeof v === 'string' && v.length > 4096)
            || e.channel !== undefined && !['text', 'reasoning'].includes(e.channel)
            || ['tool_call_id', 'tool_name', 'id', 'worker_status'].some(k => e[k] !== undefined && typeof e[k] !== 'string')
            || e.failed !== undefined && typeof e.failed !== 'boolean'
            || e.index !== undefined && (!Number.isSafeInteger(e.index) || e.index < 0))
            throw new Error('Invalid Engine pet metadata fields.');
        this.lastTime = at;
        this.events = this.events.filter(span => span.endTime >= at - 12_800);
        const id = (field) => { const s = e[field]; if (typeof s !== 'string' || !s)
            throw new Error(`Missing Engine ${field}.`); return s; };
        const index = () => { if (!Number.isSafeInteger(e.index))
            throw new Error('Missing Engine index.'); return String(e.index); };
        const start = (key, name, category, agentId) => {
            if (this.active.size >= 256 && !this.active.has(key))
                throw new Error('Too many active Engine pet spans.');
            this.active.set(key, this.add(name, category, at, agentId));
        };
        const finish = (key) => { this.pulse(key, at); this.active.delete(key); };
        switch (e.event) {
            case 'turn_started':
                this.active.clear();
                this.waiting = undefined;
                break;
            case 'message_started':
                start(`message:${index()}`, 'assistant_message', 'communication');
                this.waiting = undefined;
                break;
            case 'thinking_started':

View on GitHub (pinned to 73e0f67d83)