Hmbown/CodeWhale · error · Error
Pet segment checkpoints do not agree.
Error message
Pet segment checkpoints do not agree.
What it means
When a v2 recording has both a start checkpoint and a later checkpoint, fromRecording restores the world from the later checkpoint and cross-checks it against the start: the restored world must be segmented, its tick must not precede the start tick, and if a checkpoint exists its historyStart must equal the start tick. Any mismatch means the two checkpoints describe inconsistent segment boundaries.
Solutions
- Re-export the whole segment from a single PetWorld via prepareSegment()/recording(true, true) so start and checkpoint agree.
- Ensure chunk reassembly covers one contiguous export session (same world, uninterrupted ticks).
- Remove the mid-recording checkpoint and keep only start, letting fromRecording replay from the start.
- Verify checkpoint.historyStart equals start.tick before importing.
Example fix
// before
const mixed = { ...segA, checkpoint: segB.checkpoint }; // checkpoints from different sessions
// after
if (mixed.checkpoint.historyStart !== mixed.start.tick) throw new Error('segments do not belong together');
const world = PetWorld.fromRecording(points, { ...mixed, checkpoint: undefined }); // replay from start Defensive patterns
Strategy: validation
Validate before calling
if (r.start && r.checkpoint) {
if (r.checkpoint.historyStart !== r.start.tick) throw new Error('checkpoints disagree');
if (r.checkpoint.tick < r.start.tick) throw new Error('checkpoint precedes start');
} Type guard
const checkpointsAgree = (r: any) => !r.start || !r.checkpoint || r.checkpoint.historyStart === r.start.tick && r.checkpoint.tick >= r.start.tick;
Try / catch
try { world = PetWorld.fromRecording(points, r); }
catch (e) {
if (e.message === 'Pet segment checkpoints do not agree.') {
delete r.checkpoint; // replay from start instead of trusting the bad checkpoint
world = PetWorld.fromRecording(points, r);
} else throw e;
} Prevention
- Never merge chunks or checkpoints from two different export sessions.
- Complete prepareSegment commits atomically; discard partial archives.
- Keep segment archives immutable once written.
When it happens
Trigger: Passing a v2 recording where checkpoint.historyStart !== start.tick, where the restored world is not segmented, or where world.tick < start.tick.
Common situations: Combining chunks from two different export sessions; an interrupted archive/commit in prepareSegment leaving mismatched halves; editing ticks or historyStart by hand; re-serializing only part of the recording.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Pet segment checkpoints do not agree.
- Pet checkpoint does not match its recording.
- Pet checkpoint does not match its recording.
- Pet expression version does not match its checkpoint.
- Pet segment checkpoints do not agree.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/6fdf5b551314a1e3.
Report an issue: GitHub.
Appendix: source
Thrown at pet/ios/Resources/pet-native.js:265
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() {
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]);
const c = this.checkpoint();
c.petCheckpointVersion = 2;
c.historyStart = this.tick;
c.hasTelemetry = this.hasTelemetry;
c.bucketIndex -= dropTape;
c.interactionIndex = 0;
c.history = new PetWorld(points, tape, interactions, this.sim.expressionVersion, true).historyDigest();
const next = PetWorld.restore(points, tape, interactions, c);View on GitHub (pinned to 433685b202)