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
- Convert timestamps to session-relative ms and sort events before calling observe
- Assert Number.isFinite(at) and at >= lastTime at the call site
- Check the timestamp is not an epoch time that exceeds PET_MAX_SECONDS*1000
- 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
- Sort all events by timestamp before observing
- Convert epoch times to session-relative ms once, at ingestion
- Keep a single monotonic clock source for the session
- Validate timestamps when parsing log entries, not at observe time
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
- Invalid interval for
- Invalid Runtime retention horizon.
- Signal duration must be finite and nonnegative.
- World dt must be in [0, 10] seconds.
- 1
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)