Hmbown/CodeWhale · error · Error
Invalid pet state.
Error message
Invalid pet state.
What it means
`validatePetState(obj)` is the guard for pet telemetry frame objects. It checks that the channel is a known key in `CHANNEL_INDEX` and that every numeric field (activity, coherence, attention, observed, roamX, roamY, flip, lit, onsets entries, activeMs entries, etc.) is a finite number. Any missing field, wrong type, or non-finite value (NaN/Infinity) makes the frame invalid and the library throws.
Solutions
- Validate/sanitize the frame before submitting: check the channel key against `CHANNEL_INDEX` and coerce every numeric field with `Number()` plus a `Number.isFinite` check.
- Replace unavailable sensor readings with the documented defaults (e.g. activity .12, coherence .25, attention 0) instead of null/NaN.
- If a field or channel was renamed/added, update the producer and `CHANNELS` registration together so the frame matches the schema.
Example fix
// before
world.record(seq, { channel: 'purr', activity: NaN, coherence: .25 }); // missing fields, NaN -> throws
// after
const frame = normalizeFrame(raw); // fills defaults, coerces numbers, rejects NaN
if (CHANNEL_INDEX[frame.channel] === undefined) throw new TypeError('unknown channel: ' + frame.channel);
world.record(seq, frame); Defensive patterns
Strategy: type-guard
Validate before calling
function isValidPetStateFrame(f) { return f != null && typeof f === 'object' && f.channel in CHANNEL_INDEX && [f.activity, f.coherence, f.attention, f.observed, f.roamX, f.roamY, f.flip, f.lit, ...f.onsets, ...f.activeMs].every(Number.isFinite); } Type guard
function isPetState(v) { return v != null && typeof v === 'object' && typeof v.channel === 'string' && v.channel in CHANNEL_INDEX && [v.activity, v.coherence, v.attention, v.observed, v.roamX, v.roamY, v.flip, v.lit].every(n => Number.isFinite(n)); } Try / catch
try { world.record(seq, frame); } catch (e) { if (e.message === 'Invalid pet state.') { logCorruptFrame(seq, frame); frame = defaultFrame(seq); world.record(seq, frame); } else throw e; } Prevention
- Sanitize producer output: coerce numeric fields and replace missing readings with documented defaults, never NaN/null.
- Register new channels in CHANNELS/CHANNEL_INDEX before emitting frames with their key.
- Validate frames at the ingestion boundary (before record) so corruption is caught at the source.
- Check JSON round-trips keep numbers as numbers, not strings.
When it happens
Trigger: Submitting a frame object to PetWorld's record/append API that is missing a required field, has a channel key not present in `CHANNELS`, contains NaN/Infinity values (e.g. from a division by zero upstream), or has onsets/activeMs arrays with non-numeric entries.
Common situations: Deserializing frames from JSON where numbers came back as strings or null; adding a new channel without registering it in `CHANNELS`/`CHANNEL_INDEX`; sensor code emitting NaN when a reading is unavailable; renaming a field and breaking the frame shape.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid Engine pet metadata fields.
- Invalid event-v1 pet input. Import through importTrace…
- Invalid failure observation time.
- Invalid version 1 pet bucket.
- Invalid version 1 pet bucket.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/b2b6a2d6408fca6f.
Report an issue: GitHub.
Appendix: source
Thrown at pet/ios/Resources/pet-native.js:711
{ key: 'memory', label: 'Memory / RAG', color: '#b6a77f', freq: 195.99, sustained: true, arch: 'gyre', form: 'gyre · scanning' },
{ key: 'code', label: 'Code execution', color: '#9b9ed7', freq: 164.81, sustained: false, arch: 'strike', form: 'strike · along the body' },
{ key: 'filesystem', label: 'Filesystem', color: '#92b9c9', freq: 440.00, sustained: false, arch: 'strike', form: 'strike · fanning' },
{ key: 'network', label: 'Network / API', color: '#d3ac74', freq: 523.25, sustained: false, arch: 'cross', form: 'crossing · one way' },
{ key: 'browser', label: 'Browser / computer', color: '#9ea9df', freq: 349.23, sustained: false, arch: 'cross', form: 'crossing · a sweep' },
{ key: 'communication', label: 'Agent messages', color: '#83c5c9', freq: 293.66, sustained: false, arch: 'cross', form: 'crossing · two ways' },
{ key: 'agent', label: 'Subagent activity', color: '#b09acb', freq: 220.00, sustained: true, arch: 'pod', form: 'pod · peers' },
{ key: 'orchestration', label: 'Orchestration', color: '#6c8798', freq: 98.00, sustained: true, arch: 'pod', form: 'pod · hub' },
{ key: 'error', label: 'Errors / exceptions', color: '#e79186', freq: 185.00, sustained: false, arch: 'tear', form: 'torn · irregular' },
{ key: 'human', label: 'Human interaction', color: '#c2b787', freq: 391.99, sustained: false, arch: 'address', form: 'decision · junction' },
{ key: 'other', label: 'Unclassified', color: '#738492', freq: 146.83, sustained: false, arch: 'drift', form: 'drifting · unformed' },
];
exports.CHANNEL_INDEX = Object.fromEntries(exports.CHANNELS.map((c, i) => [c.key, i]));
function validatePetState(value) {
const s = value;
if (!s || typeof s !== 'object' || !Object.hasOwn(exports.CHANNEL_INDEX, s.channel)
|| ![s.activity, s.coherence, s.attention, s.observed, s.lit].every(n => Number.isFinite(n) && n >= 0 && n <= 1)
|| ![s.roamX, s.roamY, s.flip].every(n => Number.isFinite(n) && Math.abs(n) <= 1))
throw new Error('Invalid pet state.');
}
const hex2rgb = (h) => [parseInt(h.slice(1, 3), 16), parseInt(h.slice(3, 5), 16), parseInt(h.slice(5, 7), 16)];
const RGB = exports.CHANNELS.map(c => hex2rgb(c.color));
const UNKNOWN_RGB = hex2rgb('#738492');
const REST_RGB = [122, 214, 240];
// mulberry32 — a 32-bit seeded PRNG tiny enough to port by hand correctly.
function mulberry32(seed) {
let a = seed >>> 0;
return Object.assign(() => {
a = (a + 0x6D2B79F5) >>> 0;
let t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}, { state: () => a, restore: (state) => {
if (!Number.isSafeInteger(state) || state < 0 || state > 0xffffffff)
throw new Error('Invalid pet random stream.');
a = state;View on GitHub (pinned to 433685b202)