Hmbown/CodeWhale · error · Error

Pet checkpoint telemetry does not match its clock.

Error message

Pet checkpoint telemetry does not match its clock.

What it means

A checkpoint's frame.telemetry snapshot must exactly equal the tape bucket the checkpoint's frame.timeMs falls inside (or be undefined when the time lies outside any bucket). restore recomputes the expected telemetry from the tape and compares; a mismatch means the checkpoint's telemetry was not derived from that tape at that clock time.

Solutions

  1. Use checkpoints emitted by the library itself; do not edit frame.timeMs or frame.telemetry manually.
  2. Re-take the checkpoint from a live world so telemetry and time are regenerated together.
  3. Ensure the paired tape is the exact tape the checkpoint was captured against.

Example fix

// before
c.frame.timeMs = 12345; // edited by hand
PetWorld.restore(points, tape, interactions, c); // throws
// after
const world = PetWorld.restore(points, tape, interactions, originalC); // restore unmodified checkpoint
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const world = PetWorld.restore(points, tape, interactions, c);
} catch (e) {
  if (e.message === 'Pet checkpoint telemetry does not match its clock.') {
    // checkpoint was hand-edited or paired with the wrong tape; re-take it
  } else throw e;
}

Prevention

When it happens

Trigger: Calling PetWorld.restore(points, tape, interactions, c) where c.frame.telemetry was hand-set, computed from a different tape bucket, or the frame.timeMs was edited so it now lands in a different bucket than the recorded telemetry.

Common situations: Adjusting frame.timeMs during a migration without recomputing telemetry; copying telemetry from another tick; serialization that altered telemetry fields subtly (float formatting aside, via JSON compare).

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

Appendix: source

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

      || 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
   * replaying their sound or exposing a stale request as current work. */
  resumeObservation(): void {
    const last = this.tapeLog.at(-1), end = last ? (last.sequence + 1) * 12 : 0;

View on GitHub (pinned to 433685b202)