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

  1. Ensure the producer writes top-level expressionVersion and each checkpoint's sim.expressionVersion together.
  2. Set the recording's top-level expressionVersion to match the checkpoint's sim.expressionVersion (or vice versa).
  3. Re-export the whole recording from a single live PetWorld instead of assembling from parts.
  4. 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

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


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)