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
- Always include the required string fields: tool_call_id and tool_name for tool events, id for agent events
- Stringify numeric ids at the bridge boundary (String(id))
- Skip or repair events with empty identifiers before feeding telemetry
- 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
- Always send tool_call_id and tool_name with tool events
- Always send id with agent events
- Stringify numeric ids at the bridge boundary
- Reject empty-string identifiers at ingestion
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
- Missing Engine .
- Missing Engine index.
- Missing Engine index.
- Invalid Engine pet clock.
- Invalid Engine pet metadata.
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)