Hmbown/CodeWhale · error · Error
Invalid pet checkpoint.
Error message
Invalid pet checkpoint.
What it means
fromRecording allows start and checkpoint keys to be absent, but if a key is present it must be a truthy checkpoint object. A recording carrying `start: null` or `checkpoint: null` (present but empty) is internally inconsistent — the producer intended a segmented/checkpointed recording but the data is missing — so this error is thrown.
Solutions
- Remove the falsy start/checkpoint keys entirely from the recording object before restoring.
- Restore the missing checkpoint from the segment archive that produced it.
- Re-record or re-export the segment from a live world via prepareSegment().
- Fix the producer/serialization step to omit empty optional keys instead of writing null.
Example fix
// before
const r = JSON.parse(raw); // { ..., start: null, checkpoint: {...} }
const world = PetWorld.fromRecording(points, r); // throws
// after
const r = JSON.parse(raw);
if (r.start === null) delete r.start;
if (r.checkpoint === null) delete r.checkpoint;
const world = PetWorld.fromRecording(points, r); Defensive patterns
Strategy: validation
Validate before calling
if ('start' in r && !r.start) delete r.start;
if ('checkpoint' in r && !r.checkpoint) delete r.checkpoint; Type guard
const hasCheckpoint = (r, k) => !(k in r) || (r[k] !== null && typeof r[k] === 'object');
Try / catch
try {
world = PetWorld.fromRecording(points, r);
} catch (err) {
if (err.message === 'Invalid pet checkpoint.')
world = PetWorld.fromRecording(points, { ...r, start: undefined, checkpoint: undefined });
else throw err;
} Prevention
- Configure serializers to omit empty optional keys instead of writing null.
- Strip null-valued optional keys after JSON.parse before restoring.
- Fix producers that write placeholder checkpoint keys.
- Treat present-but-null checkpoints as a corruption signal and re-export.
When it happens
Trigger: Calling `PetWorld.fromRecording(points, r)` where r.start or r.checkpoint is explicitly present but null/undefined/0/'' (e.g. `{start: null}` or `{checkpoint: undefined}` serialized as a present-but-falsy key).
Common situations: Serialization layers that write null for missing optional fields; hand-merging segment files; a producer bug that wrote the keys without populating them; JSON round-trips through schemas that normalize undefined to null.
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 checkpoint.
- Invalid pet particle checkpoint.
- Invalid pet particle checkpoint.
- Invalid pet recording.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/9c21e6c1017fe679.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:253
}
recording(withCheckpoint = true, completed = false) {
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, value) {
const r = value;
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. */View on GitHub (pinned to 73e0f67d83)