Hmbown/CodeWhale · error · Error

Unsupported Engine pet event.

Error message

Unsupported Engine pet event.

What it means

observe dispatches on e.event and only recognizes the Engine lifecycle events it models (turn_started, message_*/thinking_*/response_delta, tool_call_*, agent_*, approval_required, user_input_required, turn_complete, error). Any other event string reaches the default case and throws 'Unsupported Engine pet event.' The adapter is deliberately strict: unknown protocol events must fail loudly rather than be silently ignored, so the pet sim never misreads activity.

Solutions

  1. Filter events to the supported set before calling observe, ignoring purely informational ones
  2. Extend the switch with the new event case when the protocol legitimately added it (and bump the adapter with the protocol change)
  3. Fix the event name spelling to match a supported variant
  4. Pre-decode with a mapping table from raw EventMsg type to adapter event names

Example fix

// before
telemetry.observe({ event: msg.type, ... }, at); // msg.type may be 'compaction_started'
// after
const SUPPORTED = new Set(['turn_started','message_started','thinking_started','response_delta','message_complete','thinking_complete','tool_call_started','tool_call_heartbeat','tool_call_complete','agent_spawned','agent_progress','agent_complete','approval_required','user_input_required','turn_complete','error']);
if (SUPPORTED.has(msg.type)) telemetry.observe({ event: msg.type, ...meta }, at);
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED = new Set(['turn_started','message_started','thinking_started','response_delta','message_complete','thinking_complete','tool_call_started','tool_call_heartbeat','tool_call_complete','agent_spawned','agent_progress','agent_complete','approval_required','user_input_required','turn_complete','error']);
if (SUPPORTED.has(msg.type)) telemetry.observe({ event: msg.type, ...project(meta) }, at);

Type guard

const isSupportedEngineEvent = (e) => typeof e?.event === 'string' && SUPPORTED.has(e.event);

Try / catch

try {
  telemetry.observe(meta, at);
} catch (err) {
  if (err.message === 'Unsupported Engine pet event.') {
    console.warn('ignoring unknown engine event', meta.event);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Feeding raw EventMsg variants not in the adapter's switch (e.g. 'session_started', 'compaction_started', 'token_count', 'shutdown'); a new protocol event added upstream before the adapter learns it; typo'd event names like 'turn_start'; replaying a foreign telemetry tape with non-Engine event names.

Common situations: Protocol version drift: the Rust side added an event kind and the JS adapter is stale; a caller piping every session log event into observe instead of only lifecycle events; renaming an event in a refactor with one call site missed.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

                    start(key, 'agent', 'agent', id('id'));
                else
                    this.pulse(key, at);
                break;
            }
            case 'agent_complete':
                finish(`agent:${id('id')}`);
                break;
            case 'approval_required':
            case 'user_input_required':
                this.waiting = this.add('human', 'human', at);
                this.waiting.status = 'pending';
                break;
            case 'turn_complete':
                this.active.clear();
                this.waiting = undefined;
                break;
            case 'error': break;
            default: throw new Error('Unsupported Engine pet event.');
        }
        // Receipt time is the error onset; never rewrite the operation's old start.
        if (e.event === 'error' || e.failed === true)
            this.add('error', 'error', at).status = 'error';
    }
    /** Waiting coverage comes from the existing typed shell's current request.
     * It can extend a witnessed request, never invent one on a mid-turn attach. */
    confirmWaiting(at, waiting) {
        if (!waiting) {
            this.waiting = undefined;
            return;
        }
        if (this.waiting && at >= this.waiting.endTime)
            this.waiting.endTime = at;
    }
    bucket(sequence) {
        const end = (sequence + 1) * pet_telemetry_js_1.PET_BIN_MS;
        const input = this.events.filter(e => e.startTime < end && e.endTime >= end - 12_400)

View on GitHub (pinned to 73e0f67d83)