{"record":{"id":"43277e752675a8db","repo":"Hmbown/CodeWhale","slug":"missing-engine-field-pet","errorCode":null,"errorMessage":"Missing Engine ${field}.","messagePattern":"Missing Engine (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-engine.ts","lineNumber":86,"sourceCode":"    next.sequence = this.sequence; next.lastTime = this.lastTime;\n    return next;\n  }\n\n  observe(value: unknown, at: number): void {\n    if (!Number.isFinite(at) || at < this.lastTime || at > PET_MAX_SECONDS * 1000) throw new Error('Invalid Engine pet clock.');\n    if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid Engine pet metadata.');\n    const e = value as Record<string, unknown>;\n    const allowed = ['event', 'index', 'channel', 'tool_call_id', 'tool_name', 'id', 'worker_status', 'failed'];\n    if (Object.keys(e).some(k => !allowed.includes(k)) || typeof e.event !== 'string'\n      || Object.values(e).some(v => typeof v === 'string' && v.length > 4096)\n      || e.channel !== undefined && !['text', 'reasoning'].includes(e.channel as string)\n      || ['tool_call_id', 'tool_name', 'id', 'worker_status'].some(k => e[k] !== undefined && typeof e[k] !== 'string')\n      || e.failed !== undefined && typeof e.failed !== 'boolean'\n      || e.index !== undefined && (!Number.isSafeInteger(e.index) || (e.index as number) < 0))\n      throw new Error('Invalid Engine pet metadata fields.');\n    this.lastTime = at;\n    this.events = this.events.filter(span => span.endTime >= at - 12_800);\n    const id = (field: string) => { const s = e[field]; if (typeof s !== 'string' || !s) throw new Error(`Missing Engine ${field}.`); return s; };\n    const index = () => { if (!Number.isSafeInteger(e.index)) throw new Error('Missing Engine index.'); return String(e.index); };\n    const start = (key: string, name: string, category: Category, agentId?: string) => {\n      if (this.active.size >= 256 && !this.active.has(key)) throw new Error('Too many active Engine pet spans.');\n      this.active.set(key, this.add(name, category, at, agentId));\n    };\n    const finish = (key: string) => { this.pulse(key, at); this.active.delete(key); };\n    switch (e.event) {\n      case 'turn_started': this.active.clear(); this.waiting = undefined; break;\n      case 'message_started': start(`message:${index()}`, 'assistant_message', 'communication'); this.waiting = undefined; break;\n      case 'thinking_started': start(`thinking:${index()}`, 'thinking', 'reasoning'); this.waiting = undefined; break;\n      case 'response_delta': {\n        const reasoning = e.channel === 'reasoning';\n        const key = `${reasoning ? 'thinking' : 'message'}:${index()}`;\n        if (!this.active.has(key)) start(key, reasoning ? 'thinking' : 'assistant_message', reasoning ? 'reasoning' : 'communication');\n        else this.pulse(key, at);\n        this.waiting = undefined; break;\n      }\n      case 'message_complete': finish(`message:${index()}`); break;","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-engine.ts#L68-L104","documentation":"The `id()` helper inside observe() reads a required string field (`tool_call_id`, `tool_name`, or `id` depending on the event branch) and throws `Missing Engine <field>.` when the field is absent, not a string, or an empty string. The event passed the general validation but is missing the specific identifier the event's switch branch requires.","triggerScenarios":"Emitting a `message_finished`/`tool` event whose `id` or `tool_call_id` is undefined, null, or ''; passing `tool_name: ''` for a tool span start; omitting the id because the upstream pairing (tool_call → result) lost the identifier.","commonSituations":"Streaming providers that omit ids on partial chunks; renaming `tool_call_id` to `callId` in your own wrapper; empty-string ids when a template substituted a missing variable; forwarding events from a queue schema that made ids optional.","solutions":["Populate the required id field for the event kind being emitted (check the switch branch in pet-engine.ts to see which field it needs).","Replace empty strings with a real identifier before calling observe(), or skip the event.","If your upstream uses a different key name, map it to the engine's expected field (e.g. callId → tool_call_id).","Log the full event object on this error to see which `field` name appears in the message — it names exactly what is missing."],"exampleFix":"// before\nengine.observe({ event: 'tool_started', tool_name: '' }, now);\n\n// after\nengine.observe({ event: 'tool_started', tool_name: 'web_search', tool_call_id: callId }, now);","handlingStrategy":"validation","validationCode":"function hasRequiredIds(e: Record<string, unknown>): boolean {\n  const need = e.event === 'tool' || e.event === 'tool_finished' ? ['tool_call_id','tool_name'] : ['id'];\n  return need.every(k => typeof e[k] === 'string' && e[k].length > 0);\n}\nif (!hasRequiredIds(event)) throw new TypeError('event missing required id field');","typeGuard":"const hasId = (e: Record<string, unknown>, field: string): e is Record<string, unknown> & { [k: string]: string } =>\n  typeof e[field] === 'string' && (e[field] as string).length > 0;","tryCatchPattern":"try {\n  engine.observe(event, at);\n} catch (err) {\n  const m = /^Missing Engine (.+)\\.$/.exec(err instanceof Error ? err.message : '');\n  if (m) {\n    console.error(`event missing field '${m[1]}'`, event);\n    return; // drop or enqueue for repair\n  }\n  throw err;\n}","preventionTips":["Carry provider-issued ids (tool_call_id, message id) through your whole pipeline; never drop them between layers.","Treat empty string as missing — validate ids with a non-empty check, not just presence.","Map your internal field names to the engine's expected names in one adapter."],"tags":["validation","missing-field","telemetry"],"backgroundTag":"missing-required-argument","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}