Hmbown/CodeWhale · error · Error
Invalid pet checkpoint.
Error message
Invalid pet checkpoint.
What it means
fromRecording validates optional checkpoint fields: if start or checkpoint keys are present they must be truthy values, otherwise the recording is malformed. Note the sibling errors 'Invalid legacy pet recording' and 'The recording segment is missing its starting checkpoint' cover version-specific checkpoint violations.
Solutions
- Remove the empty start/checkpoint key entirely (undefined means absent) instead of writing null.
- Fix the save path so a checkpoint is either fully serialized or omitted.
- Re-export the recording from the app.
- Validate checkpoints are truthy objects before loading.
Example fix
// before
save({ ...recording, checkpoint: null });
// after
save({ ...recording, checkpoint: undefined }); // or omit the key entirely Defensive patterns
Strategy: type-guard
Validate before calling
function hasValidCheckpoints(rec) { return (rec.checkpoint === undefined || !!rec.checkpoint) && (rec.start === undefined || !!rec.start); }
if (!hasValidCheckpoints(rec)) rec = { ...rec, checkpoint: rec.checkpoint || undefined, start: rec.start || undefined }; Type guard
const isCheckpoint = (c) => !!c && typeof c === 'object' && typeof c.petCheckpointVersion === 'number';
Try / catch
try { PetWorld.fromRecording(points, rec); } catch (e) { if (e.message === 'Invalid pet checkpoint.') reloadFromExport(); } Prevention
- When serializing, omit empty checkpoint keys instead of writing null.
- Use a save routine that writes checkpoint and data atomically.
When it happens
Trigger: Passing a recording where r.checkpoint is defined but null/empty, or r.start is defined but null/empty — e.g. { checkpoint: null } after a failed serialization.
Common situations: A save routine that wrote the key but failed to serialize the checkpoint; manual JSON editing that blanked a checkpoint; partial writes from a crashed process.
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 checkpoint.
- Invalid pet particle checkpoint.
- Invalid pet particle checkpoint.
- Invalid pet recording.
- Invalid pet recording.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/6db1b40713814615.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/pet-world.ts:141
frame: this.frame, voices: this.voices });
}
recording(withCheckpoint = true, completed = false): PetRecording {
const tapeEnd = completed ? this.bucketIndex + 1 : this.tapeLog.length;
const inputEnd = completed ? this.interactionIndex : this.interactionLog.length;
const history = this.historyDigest(tapeEnd, inputEnd);
return { petReplayVersion: this.segmented ? 2 : 1, expressionVersion: this.sim.expressionVersion,
tape: this.tapeLog.slice(0, tapeEnd), interactions: this.interactionLog.slice(0, inputEnd).map(e => ({ ...e })),
...(this.origin ? { start: { ...structuredClone(this.origin), hasTelemetry: this.hasTelemetry, history } } : {}),
...(withCheckpoint ? { checkpoint: { ...this.checkpoint(), history } } : {}) };
}
static fromRecording(points: [number, number][], value: unknown): PetWorld {
const r = value as PetRecording;
if (!r || ![1, 2].includes(r.petReplayVersion) || !Array.isArray(r.tape) || !Array.isArray(r.interactions)) throw new Error('Invalid pet recording.');
const version = r.expressionVersion === undefined ? 1 : r.expressionVersion;
if (![1, 2].includes(version)) throw new Error('Unsupported pet expression version.');
if (r.checkpoint !== undefined && !r.checkpoint || r.start !== undefined && !r.start) throw new Error('Invalid pet checkpoint.');
for (const c of [r.start, r.checkpoint]) if (c && (c.sim?.expressionVersion ?? 1) !== version) throw new Error('Pet expression version does not match its checkpoint.');
if (r.petReplayVersion === 1 && (r.start || r.checkpoint && r.checkpoint.petCheckpointVersion !== 1)) throw new Error('Invalid legacy pet recording.');
if (r.petReplayVersion === 2 && (!r.start || r.start.petCheckpointVersion !== 2 || r.start.historyStart !== r.start.tick)) throw new Error('The recording segment is missing its starting checkpoint.');
const first = r.start && PetWorld.restore(points, r.tape, r.interactions, r.start);
const world = r.checkpoint ? PetWorld.restore(points, r.tape, r.interactions, r.checkpoint) : first ?? new PetWorld(points, r.tape, r.interactions, version);
if (first) {
if (!world.segmented || world.tick < first.tick || r.checkpoint && r.checkpoint.historyStart !== first.tick) throw new Error('Pet segment checkpoints do not agree.');
world.origin = first.checkpoint();
}
return world;
}
/** Retire only consumed input. The exact origin makes each archived segment
* independently replayable; no particle, random stream or score is reset. */
prepareSegment(): PetSegment {
const dropTape = Math.max(0, this.bucketIndex), dropInputs = this.interactionIndex, previous = this.origin;
const tape = this.tapeLog.slice(dropTape), interactions = this.interactionLog.slice(dropInputs);
const points = this.sim.p.map(p => [p.hx, p.hy] as [number, number]);View on GitHub (pinned to 433685b202)