Hmbown/CodeWhale · error · Error

Invalid pet recording.

Error message

Invalid pet recording.

What it means

fromRecording is the trusted entry point for loading a serialized PetRecording. The value must be a truthy object with petReplayVersion 1 or 2, and array-valued tape and interactions. Anything else throws this error.

Solutions

  1. Parse the file with JSON.parse before passing it to fromRecording.
  2. Check recording.petReplayVersion is 1 or 2 and that tape and interactions are arrays.
  3. Re-export the recording from a compatible app version.
  4. Validate the recording shape before calling fromRecording.

Example fix

// before
const world = PetWorld.fromRecording(points, localStorage.getItem('rec'));
// after
const raw = JSON.parse(localStorage.getItem('rec'));
const world = PetWorld.fromRecording(points, raw);
Defensive patterns

Strategy: type-guard

Validate before calling

function looksLikeRecording(v) { return !!v && typeof v === 'object' && [1,2].includes(v.petReplayVersion) && Array.isArray(v.tape) && Array.isArray(v.interactions); }
if (!looksLikeRecording(parsed)) throw new Error('Not a pet recording');

Type guard

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

Try / catch

try { world = PetWorld.fromRecording(points, value); } catch (e) { if (e.message === 'Invalid pet recording.') showCorruptRecordingDialog(); }

Prevention

When it happens

Trigger: Passing null/undefined, a JSON string instead of a parsed object, petReplayVersion 3 or missing, or tape/interactions that are not arrays (or absent) to PetWorld.fromRecording(points, value).

Common situations: Loading a file saved by a newer app version with an unknown replay version; forgetting JSON.parse; a corrupted or truncated recording file; passing an old export shape without tape/interactions.

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

Appendix: source

Thrown at pet/src/core/pet-world.ts:138

      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): PetRecording {
    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: [number, number][], value: unknown): PetWorld {
    const r = value as PetRecording;
    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(): PetSegment {

View on GitHub (pinned to 433685b202)