Hmbown/CodeWhale · error · Error
Invalid pet state.
Error message
Invalid pet state.
What it means
validatePetState() checks that a restored or simulated pet state object has a known channel key and that all normalized fields (activity, coherence, attention, observed, lit in [0,1]; roamX, roamY, flip within ±1) are finite numbers in range. It is called from checkpoint restore (error 20's constructor) and guards the renderer against malformed state that would break drawing or audio math. Any structural or range violation throws immediately.
Solutions
- Clamp all normalized fields into range and coerce to numbers before validation: `n = Number(n); Math.min(1, Math.max(0, n))`.
- Ensure s.channel is one of the current CHANNELS keys; migrate state saved against an older channel list.
- Regenerate the state from a fresh simulation if the source checkpoint is corrupt.
- Log the offending field at the call site to identify which numeric constraint failed.
Example fix
// before world.sim.restore(saved.sim); // saved.attention = 1.7 -> throws // after for (const k of ['activity','coherence','attention','observed','lit']) saved.sim.state[k] = Math.min(1, Math.max(0, Number(saved.sim.state[k]))); saved.sim.state.channel = CHANNELS.some(c => c.key === saved.sim.state.channel) ? saved.sim.state.channel : 'other'; world.sim.restore(saved.sim);
Defensive patterns
Strategy: type-guard
Validate before calling
function isPetState(s, CHANNEL_INDEX) {
const in01 = n => typeof n === 'number' && Number.isFinite(n) && n >= 0 && n <= 1;
const inPM1 = n => typeof n === 'number' && Number.isFinite(n) && Math.abs(n) <= 1;
return !!s && typeof s === 'object' && s.channel in CHANNEL_INDEX &&
[s.activity, s.coherence, s.attention, s.observed, s.lit].every(in01) &&
[s.roamX, s.roamY, s.flip].every(inPM1);
} Type guard
const isValidPetState = (s) => !!s && typeof s === 'object' && typeof s.channel === 'string' && [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);
Try / catch
try {
validatePetState(state);
} catch (e) {
if (e.message === 'Invalid pet state.') {
state = defaultPetState(); // reset to a known-good state
} else throw e;
} Prevention
- Clamp normalized fields to [0,1] and roam/flip to ±1 at every producer
- Coerce serialized values back to numbers before validation
- Keep the channel key in sync with the current CHANNELS list
- Guard sim math against divide-by-zero that yields NaN fields
When it happens
Trigger: Calling validatePetState(value) — directly or via PetWorld checkpoint restore — with null/undefined, an object whose s.channel is not a key of CHANNEL_INDEX, any of activity/coherence/attention/observed/lit outside [0,1] or non-finite, or roamX/roamY/flip with |n| > 1 or NaN.
Common situations: Restoring checkpoints saved by a version whose channel set changed (renamed/removed channels); a sim bug producing NaN attention after a divide-by-zero; hand-edited state JSON with values like activity: 1.5; loading state serialized as strings.
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 pet score checkpoint.
- Invalid pet score checkpoint.
- 1
- Invalid coordination ledger
- Invalid pet particle checkpoint.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/cb099ee5de08e6d7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/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 73e0f67d83)