Hmbown/CodeWhale · error · Error
Pet expression version does not match its checkpoint.
Error message
Pet expression version does not match its checkpoint.
What it means
Each embedded checkpoint (start and checkpoint) carries its own sim.expressionVersion, which must equal the recording's top-level expressionVersion (defaulting to 1). This error means the recording's metadata and its embedded checkpoint disagree, so restoring could mix incompatible sim encodings.
Solutions
- Ensure the producer writes top-level expressionVersion and each checkpoint's sim.expressionVersion together.
- Set the recording's top-level expressionVersion to match the checkpoint's sim.expressionVersion (or vice versa).
- Re-export the whole recording from a single live PetWorld instead of assembling from parts.
- If importing, reject the file at load time and ask the user to re-export from the matching app version.
Example fix
// before recording.expressionVersion = 2; // checkpoint still has sim.expressionVersion: 1 // after recording.expressionVersion = recording.checkpoint.sim.expressionVersion; const world = PetWorld.fromRecording(points, recording);
Defensive patterns
Strategy: validation
Validate before calling
const version = r.expressionVersion ?? 1;
for (const c of [r.start, r.checkpoint]) if (c && (c.sim?.expressionVersion ?? 1) !== version) throw new Error('version mismatch'); Type guard
const versionsAgree = (r: any) => {
const v = r.expressionVersion ?? 1;
return [r.start, r.checkpoint].every(c => !c || (c.sim?.expressionVersion ?? 1) === v);
}; Try / catch
try { world = PetWorld.fromRecording(points, r); }
catch (e) {
if (e.message === 'Pet expression version does not match its checkpoint.') {
const cp = r.checkpoint ?? r.start;
r.expressionVersion = cp.sim.expressionVersion; // realign, then retry
world = PetWorld.fromRecording(points, r);
} else throw e;
} Prevention
- Always export recordings from a single live PetWorld — never splice checkpoints between recordings.
- Bump top-level and per-checkpoint version fields atomically in the producer.
- Test imports against recordings from each supported app version.
When it happens
Trigger: Passing a recording whose c.sim?.expressionVersion (on start or checkpoint) differs from the top-level expressionVersion — including the case where the top level defaults to 1 while the checkpoint carries 2.
Common situations: Splicing a checkpoint from one recording into another; partially upgrading recordings where only the top-level version field was bumped; copy/pasting checkpoints between exports during debugging.
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
- 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.
- Pet segment checkpoints do not agree.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/8446191f6bacd555.
Report an issue: GitHub.
Appendix: source
Thrown at pet/ios/Resources/pet-native.js:256
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. */
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);View on GitHub (pinned to 433685b202)