Hmbown/CodeWhale · error · Error

Pet checkpoint does not match its recording.

Error message

Pet checkpoint does not match its recording.

What it means

After structural validation, restore replays the tape/interactions and checks the checkpoint is consistent with the recording it claims to summarize: no unconsumed tape bucket before/at the checkpoint time may carry observed events, prior interactions must not exceed frame time, and the next interaction must not already be due. A mismatch means the checkpoint was not taken at that exact point of that recording.

Solutions

  1. Restore using the tape and interaction log that were captured together with the checkpoint (as in the original recording blob).
  2. Re-take the checkpoint (world.checkpoint(2)) against the current tape if the recording was legitimately modified.
  3. Verify storage did not reorder or drop tape buckets/interactions between capture and restore.

Example fix

// before
PetWorld.restore(points, tapeFromOtherSession, interactions, c); // throws
// after
const rec = JSON.parse(world.recording());
PetWorld.restore(points, rec.tape, rec.interactions, c);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure checkpoint and tape came from the same recording blob:
if (!recordingContainsCheckpoint(rec, c)) throw new Error('checkpoint does not belong to this recording');

Type guard

function isCheckpointOf(rec: PetRecording, c: PetCheckpoint): boolean {
  return rec.checkpoint === c || rec.start === c || (rec.checkpoint && JSON.stringify(rec.checkpoint) === JSON.stringify(c));
}

Try / catch

try {
  const world = PetWorld.restore(points, rec.tape, rec.interactions, c);
} catch (e) {
  if (e.message === 'Pet checkpoint does not match its recording.') {
    // re-pair checkpoint with its original tape/interactions
  } else throw e;
}

Prevention

When it happens

Trigger: Calling PetWorld.restore(points, tape, interactions, c) with a checkpoint that belongs to a different tape/interaction log, a tape truncated or extended after the checkpoint was taken, or interactions reordered/inserted before interactionIndex.

Common situations: Pairing a checkpoint from session A with the tape from session B; editing the tape to drop buckets without re-taking the checkpoint; reordering interaction events during storage migration.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

      || c.frame.food !== null && (!c.frame.food || !range(c.frame.food.x, -1, 1) || !range(c.frame.food.y, -1, 1.21) || !range(c.frame.food.life, 0, 1))
      || JSON.stringify(c.frame.pod) !== JSON.stringify(c.members)
      || !Array.isArray(c.voices) || c.voices.length > 1024
      || c.voices.some(v => !v || typeof v.id !== 'string' || v.id.length > 256))
      throw new Error('Invalid pet world checkpoint.');
    validatePetState(c.frame.state);
    renderPetPCM(c.voices, 0, 0);
    const world = new PetWorld(points, tape, interactions, c.sim?.expressionVersion ?? 1, c.petCheckpointVersion === 2);
    if (c.petCheckpointVersion === 2) { world.hasTelemetry = c.hasTelemetry!; world.origin = structuredClone(c); }
    if (c.history !== world.historyDigest()
      || c.bucketIndex >= 0 && world.tape[c.bucketIndex].simTimeMs > c.frame.timeMs + 1e-7
      // acceptTelemetry may fill past gaps after the most recent fixed tick.
      // Preserve that pending cursor exactly; it may lag only over empty gaps.
      || world.tape.some((b, i) => i > c.bucketIndex && b.simTimeMs <= c.frame.timeMs + 1e-7
        && (b.observed !== 0 || b.channel !== 'other' || b.errors || b.waiting || b.agentIds.length
          || b.onsets.some(Boolean) || b.activeMs.some(Boolean)))
      || world.interactionLog.slice(0, c.interactionIndex).some(e => e.timeMs > c.frame.timeMs + 1e-7)
      || world.interactionLog[c.interactionIndex]?.timeMs <= c.frame.timeMs + 1e-7)
      throw new Error('Pet checkpoint does not match its recording.');
    const candidate = world.tape[c.bucketIndex];
    const telemetry = candidate && c.frame.timeMs < candidate.simTimeMs + candidate.durationMs ? candidate : undefined;
    if (JSON.stringify(c.frame.telemetry) !== JSON.stringify(telemetry)) throw new Error('Pet checkpoint telemetry does not match its clock.');
    world.sim.restore(c.sim); world.score.restore(c.score); world.random.restore(c.random);
    world.accumulator = c.accumulator; world.tick = c.tick; world.bucketIndex = c.bucketIndex;
    world.interactionIndex = c.interactionIndex; world.branchTick = c.branchTick;
    world.behaviour = c.behaviour; world.until = c.until; world.targetX = c.targetX; world.targetY = c.targetY;
    world.x = c.x; world.y = c.y; world.flip = c.flip; world.lit = c.lit;
    world.lastActivity = c.lastActivity; world.addressedAt = c.addressedAt ?? -Infinity; world.waitSince = c.waitSince;
    world.food = structuredClone(c.food); world.members = new Map(c.members.map(m => [m.id, { ...m }]));
    world.lastStill = c.lastStill;
    // JSON omits undefined properties; keep the same frame shape as makeFrame.
    world.frame = { ...structuredClone(c.frame), telemetry: telemetry ? structuredClone(telemetry) : undefined };
    world.voices = structuredClone(c.voices);
    return world;
  }

  /** Resume observation beyond all already accepted live packets, without

View on GitHub (pinned to 433685b202)