Hmbown/CodeWhale · error · Error
Invalid legacy pet recording.
Error message
Invalid legacy pet recording.
What it means
Legacy (petReplayVersion 1) recordings must have no start checkpoint, and if a checkpoint is present it must have petCheckpointVersion 1. This error means a v1 recording carries v2-style segmented data (a start checkpoint, or a v2 checkpoint), which the legacy restore path cannot interpret.
Solutions
- Set petReplayVersion to 2 if the recording actually has a start checkpoint (i.e., it is a v2 segment).
- Strip start and any v2 checkpoint to make it a genuine legacy recording, keeping only checkpoint with petCheckpointVersion 1.
- Re-export from the current app so petReplayVersion matches the checkpoint structure.
- Fix the exporter to derive petReplayVersion from the presence of a start checkpoint.
Example fix
// before legacy.petReplayVersion = 1; // but legacy.start exists // after legacy.petReplayVersion = legacy.start ? 2 : 1; const world = PetWorld.fromRecording(points, legacy);
Defensive patterns
Strategy: validation
Validate before calling
if (r.petReplayVersion === 1 && (r.start || (r.checkpoint && r.checkpoint.petCheckpointVersion !== 1)))
throw new Error('legacy recording carries segmented data'); Type guard
const isConsistentLegacy = (r: any) => r.petReplayVersion !== 1 || (!r.start && (!r.checkpoint || r.checkpoint.petCheckpointVersion === 1));
Try / catch
try { world = PetWorld.fromRecording(points, r); }
catch (e) {
if (e.message === 'Invalid legacy pet recording.') {
r.petReplayVersion = r.start ? 2 : 1; // correct the version, retry
world = PetWorld.fromRecording(points, r);
} else throw e;
} Prevention
- Derive petReplayVersion from the checkpoint structure at export time, never hardcode it.
- Never downgrade petReplayVersion when migrating old saves without stripping v2 fields.
- Keep a migration test that round-trips both v1 and v2 recordings.
When it happens
Trigger: Passing a recording with petReplayVersion 1 that has a truthy r.start, or whose r.checkpoint.petCheckpointVersion !== 1.
Common situations: Downgrading the petReplayVersion field of a v2 segmented recording to 1 without stripping start/checkpoint; old app builds reading new saves whose fields got merged; manual data migration mistakes.
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
- codewhale_config::LEGACY_ANTIGRAVITY_TOMBSTONE_MESSAGE
- Invalid legacy pet recording.
- Antigravity is a retired, non-runnable legacy provider…
- Antigravity is a retired, non-runnable legacy provider…
- Antigravity is a retired, non-runnable legacy provider…
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/55474e08b205c602.
Report an issue: GitHub.
Appendix: source
Thrown at pet/ios/Resources/pet-native.js:258
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);
const points = this.sim.p.map(p => [p.hx, p.hy]);
const c = this.checkpoint();View on GitHub (pinned to 433685b202)