Hmbown/CodeWhale · error · Error

Invalid pet recording.

Error message

Invalid pet recording.

What it means

PetWorld.fromRecording validates a serialized pet replay before restoring world state. This error means the value passed as the recording is null, or fails the most basic shape check: it must carry a petReplayVersion of 1 or 2 plus array-valued 'tape' and 'interactions' fields. The library throws early rather than restoring a world from corrupt or foreign data.

Solutions

  1. Log the parsed value and confirm petReplayVersion, tape, and interactions are present with the right types before calling fromRecording.
  2. Re-export the recording via world.recording() / recordingChunk() and reassemble chunks 0..N in order (including the completed=true final chunk).
  3. Check the code path that produced the JSON: it must be a full pet recording, not a checkpoint or partial slice.
  4. If importing old data, verify the producer's petReplayVersion; recordings without petReplayVersion cannot be imported.

Example fix

// before
const world = PetWorld.fromRecording(points, JSON.parse(savedText));
// after
const r = JSON.parse(savedText);
if (!r || ![1,2].includes(r.petReplayVersion) || !Array.isArray(r.tape) || !Array.isArray(r.interactions))
  throw new Error('Not a valid pet recording: ' + JSON.stringify(Object.keys(r ?? {})));
const world = PetWorld.fromRecording(points, r);
Defensive patterns

Strategy: validation

Validate before calling

function isPetRecording(v) { return !!v && [1,2].includes(v.petReplayVersion) && Array.isArray(v.tape) && Array.isArray(v.interactions); }

Type guard

const isPetRecording = (v: unknown): v is PetRecording =>
  typeof v === 'object' && v !== null && [1,2].includes((v as any).petReplayVersion)
  && Array.isArray((v as any).tape) && Array.isArray((v as any).interactions);

Try / catch

let world;
try { world = PetWorld.fromRecording(points, recording); }
catch (e) { if (e.message === 'Invalid pet recording.') throw new Error('Corrupt replay: re-export required'); throw e; }

Prevention

When it happens

Trigger: Calling PetWorld.fromRecording(points, value) where value is null/undefined, value.petReplayVersion is missing or not 1 or 2, or value.tape / value.interactions is missing or not an array.

Common situations: Truncated or hand-edited export chunks; concatenating recordingChunk output in the wrong order so JSON.parse yields garbage; passing a recording produced by a different (older/newer) build whose top-level keys changed; passing a checkpoint object instead of the full recording.

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/01490fce75a16e45. Report an issue: GitHub.

Appendix: source

Thrown at pet/ios/Resources/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 433685b202)