Hmbown/CodeWhale · error · Error
Invalid pet recording.
Error message
Invalid pet recording.
What it means
PetWorld.fromRecording rehydrates a world from a serialized recording object. It first requires a truthy object with petReplayVersion of 1 or 2 and array-valued tape and interactions fields. If the value is null, not an object, has an unknown petReplayVersion, or lacks tape/interactions arrays, the recording cannot be restored and this error is thrown.
Solutions
- Check the recording object before calling: it must be truthy, have petReplayVersion 1 or 2, and array tape and interactions.
- Re-export the recording with recordingChunk() from a live world if the file predates the current schema.
- Verify the file was fully written (all chunks consumed) before parsing/restoring.
- Confirm you are passing the recording envelope, not a checkpoint or chunk fragment.
Example fix
// before
const world = PetWorld.fromRecording(points, JSON.parse(raw));
// after
const r = JSON.parse(raw);
if (!r || ![1, 2].includes(r.petReplayVersion) || !Array.isArray(r.tape) || !Array.isArray(r.interactions))
throw new Error('Corrupt pet recording file');
const world = PetWorld.fromRecording(points, r); Defensive patterns
Strategy: type-guard
Validate before calling
function isPetRecordingEnvelope(r) {
return !!r && typeof r === 'object' &&
[1, 2].includes(r.petReplayVersion) &&
Array.isArray(r.tape) && Array.isArray(r.interactions);
} Type guard
const isRecording = (v) => typeof v === 'object' && v !== null && (v.petReplayVersion === 1 || v.petReplayVersion === 2) && Array.isArray(v.tape) && Array.isArray(v.interactions);
Try / catch
let world;
try {
world = PetWorld.fromRecording(points, parsed);
} catch (err) {
if (err.message === 'Invalid pet recording.')
throw new Error(`Recording file corrupt or unsupported: ${path}`);
throw err;
} Prevention
- Validate the parsed JSON envelope before restoring.
- Ensure export writes all recordingChunk() pieces and verifies completeness before saving.
- Only feed fromRecording the full envelope, never a chunk fragment or checkpoint.
- Store petReplayVersion alongside the file and check it before parse.
When it happens
Trigger: Calling `PetWorld.fromRecording(points, value)` where value is null/undefined, value.petReplayVersion is missing or not 1 or 2, value.tape is not an array, or value.interactions is not an array.
Common situations: Loading a pet recording file that was truncated or corrupted, feeding JSON parsed from a non-pet file, passing a legacy recording with a petReplayVersion newer than the running binary supports, passing the wrong object (e.g. a checkpoint instead of the recording envelope).
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 checkpoint.
- Invalid pet interaction.
- Invalid pet particle checkpoint.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f3b8aa2a8bc2e01a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:248
random: this.random.state(), behaviour: this.behaviour, until: this.until,
targetX: this.targetX, targetY: this.targetY, x: this.x, y: this.y, flip: this.flip, lit: this.lit,
lastActivity: this.lastActivity, addressedAt: Number.isFinite(this.addressedAt) ? this.addressedAt : null,
waitSince: this.waitSince, food: this.food, members: [...this.members.values()], lastStill: this.lastStill,
frame: this.frame, voices: this.voices });
}
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();View on GitHub (pinned to 73e0f67d83)