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

  1. Clamp all normalized fields into range and coerce to numbers before validation: `n = Number(n); Math.min(1, Math.max(0, n))`.
  2. Ensure s.channel is one of the current CHANNELS keys; migrate state saved against an older channel list.
  3. Regenerate the state from a fresh simulation if the source checkpoint is corrupt.
  4. 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

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


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)