Hmbown/CodeWhale · error · Error

Invalid Engine pet clock.

Error message

Invalid Engine pet clock.

What it means

The Engine pet observer's observe(value, at) validates the observation timestamp. 'at' must be a finite number, >= the last observed time (monotonic clock), and <= PET_MAX_SECONDS * 1000; otherwise 'Invalid Engine pet clock.' is thrown to keep the simulated pet timeline consistent.

Solutions

  1. Convert timestamps to session-relative ms and sort events before calling observe
  2. Assert Number.isFinite(at) and at >= lastTime at the call site
  3. Check the timestamp is not an epoch time that exceeds PET_MAX_SECONDS*1000
  4. Drop or clamp out-of-order events instead of feeding them to observe

Example fix

// before
engine.observe(meta, entry.wallClockMs);
// after
const at = entry.offsetMs;
if (!Number.isFinite(at) || at < engine.lastTime || at > PET_MAX_SECONDS * 1000) return;
engine.observe(meta, at);
Defensive patterns

Strategy: validation

Validate before calling

function validClock(at, engine, PET_MAX_SECONDS) {
  return Number.isFinite(at) && at >= engine.lastTime && at <= PET_MAX_SECONDS * 1000;
}

Type guard

const isSessionMs = (t) => typeof t === 'number' && Number.isFinite(t) && t >= 0;

Try / catch

try {
  engine.observe(meta, at);
} catch (err) {
  if (err.message === 'Invalid Engine pet clock.') {
    console.warn('non-monotonic or out-of-range timestamp, clamping', at, '>=', engine.lastTime);
    engine.observe(meta, Math.max(engine.lastTime, Math.min(at, PET_MAX_SECONDS * 1000)));
  } else throw err;
}

Prevention

When it happens

Trigger: observe() called with at = NaN/undefined, a timestamp earlier than a previous observation (out-of-order events), or a timestamp beyond PET_MAX_SECONDS*1000.

Common situations: Feeding logs whose timestamps are not monotonic (replayed or reordered journal entries), passing Date.now() (ms epoch) instead of session-relative ms, or corrupted/missing time fields in session metadata.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/3e386ded2c7e84bc. Report an issue: GitHub.

Appendix: source

Thrown at pet/ios/Resources/pet-native.js:1729

    }
    /** Transactional batch copy; failed validation cannot accept half a packet. */
    clone() {
        const next = new PetEngineTelemetry();
        const copy = (value) => JSON.parse(JSON.stringify(value));
        next.events = copy(this.events);
        const spans = new Map(next.events.map(event => [event.id, event]));
        // Active and waiting spans must still reference their journal entry so a
        // later heartbeat extends the coverage consumed by bucket().
        const span = (event) => spans.get(event.id) ?? copy(event);
        next.active = new Map(Array.from(this.active, ([key, event]) => [key, span(event)]));
        next.waiting = this.waiting ? span(this.waiting) : undefined;
        next.sequence = this.sequence;
        next.lastTime = this.lastTime;
        return next;
    }
    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) => {

View on GitHub (pinned to 433685b202)