Hmbown/CodeWhale · error · Error
Invalid pet score checkpoint.
Error message
Invalid pet score checkpoint.
What it means
This score-voice scheduler keeps a checkpoint of its playback position as a 3-element array [lastWindow, lastSequence, lastAddress] and validates it on restore(). The stored window and sequence must be safe integers in [-1, PET_MAX_SECONDS * 2.5] and the address flag a boolean; otherwise it throws 'Invalid pet score checkpoint.' to avoid resuming playback from a corrupt position.
Solutions
- Guard the restore call: only call restore() with a value previously returned by checkpoint(), else skip and start from the beginning.
- Validate before restoring — same predicate as the library (Array length 3, safe integers in range, boolean) — and discard invalid checkpoints.
- Clear the stale persisted checkpoint after a version upgrade that changes PET_MAX_SECONDS or the checkpoint shape.
- Check the storage layer for JSON serialization of NaN/Infinity becoming null, and store sanitized values.
Example fix
// before petScore.restore(saved.checkpoint); // may be null/corrupt // after const cp = saved.checkpoint; const ok = Array.isArray(cp) && cp.length === 3 && cp.slice(0, 2).every(n => Number.isSafeInteger(n) && n >= -1 && n <= PET_MAX_SECONDS * 2.5) && typeof cp[2] === 'boolean'; if (ok) petScore.restore(cp);
Defensive patterns
Strategy: try-catch
Validate before calling
const isValidCheckpoint = (v, maxSeconds) => Array.isArray(v) && v.length === 3 && v.slice(0, 2).every(n => Number.isSafeInteger(n) && n >= -1 && n <= maxSeconds * 2.5) && typeof v[2] === 'boolean';
Type guard
const isScoreCheckpoint = (v) => Array.isArray(v) && v.length === 3 && Number.isSafeInteger(v[0]) && Number.isSafeInteger(v[1]) && v[0] >= -1 && v[1] >= -1 && typeof v[2] === 'boolean';
Try / catch
try {
score.restore(saved);
} catch (e) {
if (e.message === 'Invalid pet score checkpoint.') {
// fall back to a fresh scheduler instead of a corrupt position
score = createPetScore();
} else {
throw e;
}
} Prevention
- Only restore values obtained from checkpoint() — check for null before calling
- Sanitize persisted checkpoints: JSON turns NaN/Infinity into null, which fails restore
- Invalidate stored checkpoints after upgrading PET_MAX_SECONDS or the checkpoint shape
- Validate with the same predicate the library uses before calling restore
When it happens
Trigger: Calling restore() with null/undefined (no checkpoint saved yet), an array of the wrong length, a JSON-round-tripped value where numbers became strings, NaN from a corrupted store, or an out-of-range sequence from an older build with a different PET_MAX_SECONDS.
Common situations: Persisting the checkpoint to disk/config and loading it after an upgrade that changed PET_MAX_SECONDS; a JSON serializer turning Infinity/NaN into null; restoring before the first checkpoint() call; handing restore() the wrong object (e.g. the whole state blob instead of the 3-tuple).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid pet score checkpoint.
- Invalid pet state.
- 1
- Invalid coordination ledger
- Invalid pet particle checkpoint.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/572c2b88f5dcf94d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:1567
factories["pet-audio"]=function(exports,require){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PetScore = void 0;
exports.renderPetPCM = renderPetPCM;
const pet_sim_js_1 = require("./pet-sim.js");
const model_js_1 = require("./model.js");
/** Core emits score events; WebAudio / AVAudioEngine only present their PCM.
* Calling at any display cadence produces the same score when all world ticks
* are supplied. Calling twice for a world tick cannot retrigger a voice. */
class PetScore {
lastWindow = -1;
lastSequence = -1;
lastAddress = false;
checkpoint() { return [this.lastWindow, this.lastSequence, this.lastAddress]; }
restore(value) {
if (!Array.isArray(value) || value.length !== 3
|| !value.slice(0, 2).every(n => Number.isSafeInteger(n) && n >= -1 && n <= pet_sim_js_1.PET_MAX_SECONDS * 2.5) || typeof value[2] !== 'boolean')
throw new Error('Invalid pet score checkpoint.');
[this.lastWindow, this.lastSequence, this.lastAddress] = value;
}
voices(frame) {
const time = frame.timeMs / 1000, window = Math.floor((frame.timeMs + 1e-7) / 400);
const out = [];
const add = (id, frequency, duration, gain, pan = 0, delay = 0, kind = 'tone') => out.push({ id, start: time + delay, duration, frequency, gain, pan, kind });
const t = frame.telemetry, fresh = t !== undefined && t.sequence !== this.lastSequence;
if (fresh) {
this.lastSequence = t.sequence;
for (let c = 0; c < pet_sim_js_1.CHANNELS.length; c++) {
const channel = pet_sim_js_1.CHANNELS[c], n = t.onsets[c];
if (!n || channel.sustained || ['human', 'error'].includes(channel.key))
continue;
add(`onset:${t.sequence}:${c}`, channel.freq, .24, .035 * Math.min(2, Math.sqrt(n)), (c / 12 - .5) * .7);
}
if (t.errors)
add(`tear:${t.sequence}`, pet_sim_js_1.CHANNELS.find(c => c.key === 'error').freq, .22, .05, 0, 0, 'noise');
}View on GitHub (pinned to 73e0f67d83)