Hmbown/CodeWhale · error · Error

Pet checkpoint exceeds its size limit.

Error message

Pet checkpoint exceeds its size limit.

What it means

restoreCheckpoint(text) accepts a serialized checkpoint and, as a guard before parsing, enforces a hard 512 KiB limit on the input string. Larger checkpoints are rejected up front because checkpoints are meant to be small state snapshots, and oversized ones usually indicate corruption or the wrong payload being passed.

Solutions

  1. Pass the exact string returned by pet.checkpoint(), not recording output
  2. Check text.length <= 512*1024 before calling and split/truncate the payload if it is a composite blob
  3. Re-serialize the checkpoint compactly (strip debug fields, minify JSON)
  4. If you genuinely need more state, store it via restoreRecording (8 MiB limit) instead

Example fix

// before
pet.restoreCheckpoint(bigBlob);
// after
if (bigBlob.length > 512 * 1024) throw new Error('not a checkpoint?');
pet.restoreCheckpoint(bigBlob);
Defensive patterns

Strategy: validation

Validate before calling

function canRestoreCheckpoint(text) { return typeof text === 'string' && text.length <= 512 * 1024; }

Try / catch

try {
  pet.restoreCheckpoint(text);
} catch (err) {
  if (err.message === 'Pet checkpoint exceeds its size limit.') {
    // wrong payload or corrupted checkpoint — recover from last good checkpoint
    pet.restoreCheckpoint(lastGoodCheckpoint);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling restoreCheckpoint with a string longer than 512*1024 characters — e.g. passing a full recording/habitat dump instead of a checkpoint, or a checkpoint serialized with an accidentally embedded recording.

Common situations: Mixing up checkpoint() output with recording() output; a producer bug that nests recordings inside checkpoints; checkpoints inflated by verbose debug fields.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/03d45bfe5a014294. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/pet-native.ts:58

  }
  /** Losing a producer invalidates outstanding coverage, never the creature. */
  disconnectEngine(): void { this.engine = new PetEngineTelemetry(); this.world.voices = []; }
  interact(kind: PetInteraction['kind'], x: number, y: number): void { this.world.interact(kind, x, y); }
  interactions(): string { return JSON.stringify(this.world.interactions); }
  accept(packet: string): void { this.world.acceptTelemetry(JSON.parse(packet)); }
  acceptLiveTail(text: string): boolean {
    const packet = this.liveTape.readTail(text); if (!packet) return false;
    this.world.acceptTelemetry(packet); return true;
  }
  resetLiveInput(): number { this.liveTape.reset(); return this.resumeEngine(); }
  recording(withCheckpoint = false): string { return JSON.stringify(this.world.recording(withCheckpoint)); }
  needsSegment(): boolean { return this.world.needsSegment; }
  prepareSegment(): string { this.segment = this.world.prepareSegment(); return JSON.stringify(this.segment.recording); }
  commitSegment(): void { if (!this.segment) throw new Error('No pet segment was prepared.'); this.segment.commit(); this.segment = undefined; }
  checkpoint(): string { return JSON.stringify(this.world.checkpoint()); }
  recordingChunk(index: number, completed = false): string | null { return this.world.recordingChunk(index, completed); }
  restoreCheckpoint(text: string): void {
    if (text.length > 512 * 1024) throw new Error('Pet checkpoint exceeds its size limit.');
    this.restoreRecording(JSON.stringify({ ...this.world.recording(false), checkpoint: JSON.parse(text) }));
  }
  restoreRecording(text: string): void {
    if (text.length > 8 * 1024 * 1024) throw new Error('Native habitat exceeds 8 MiB.');
    const points = this.world.sim.p.map(p => [p.hx, p.hy] as [number, number]);
    this.world = PetWorld.fromRecording(points, JSON.parse(text));
    this.liveTape.reset();
    this.segment = undefined; this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000); this.engine = new PetEngineTelemetry();
  }
  /** Resume a live host at the first unrecorded bucket. Keep every accepted
   * interval, but never present its last observed frame as current evidence. */
  resumeEngine(): number {
    this.world.resumeObservation();
    this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);
    this.engine = new PetEngineTelemetry();
    this.world.voices = [];
    return this.world.frame.timeMs;
  }

View on GitHub (pinned to 433685b202)