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

After the cursor check passes, PetWorld re-derives which tape bucket should hold the checkpoint's telemetry: the bucket at bucketIndex whose sim clock window (simTimeMs..simTimeMs+durationMs) contains frame.timeMs. It then compares that bucket (or undefined when the frame falls past the bucket's window) byte-for-byte against the telemetry snapshotted in the checkpoint. A mismatch means the checkpoint's stored telemetry frame was not the one the tape would replay at that time — the checkpoint and the simulation clock disagree.

Solutions

  1. Take a new checkpoint after accepting or splicing telemetry; do not restore older ones
  2. Verify the checkpoint's telemetry frame is JSON-identical to world.tape[bucketIndex] for the same timeMs before restoring
  3. Rebuild the tape from the same recording the checkpoint was made from
  4. Persist checkpoint and tapeLog together so they cannot diverge

Example fix

// before
world.restoreCheckpoint(cpFromYesterday); // tape bucket since overwritten
// after
const bucket = world.tape[cpFromYesterday.bucketIndex];
if (bucket && JSON.stringify(cpFromYesterday.frame.telemetry) === JSON.stringify(bucket)) {
  world.restoreCheckpoint(cpFromYesterday);
} else {
  world.restoreCheckpoint(world.checkpoint());
}
Defensive patterns

Strategy: validation

Validate before calling

function telemetryMatches(world, c) {
  const b = world.tape[c.bucketIndex];
  const expected = b && c.frame.timeMs < b.simTimeMs + b.durationMs ? b : undefined;
  return JSON.stringify(c.frame.telemetry) === JSON.stringify(expected);
}

Try / catch

try {
  world.restoreCheckpoint(cp);
} catch (e) {
  if (e.message.includes('telemetry does not match')) cp = world.checkpoint();
  else throw e;
}

Prevention

When it happens

Trigger: Restoring a checkpoint whose frame.telemetry was captured from a different tape bucket than bucketIndex — typically after segmented acceptTelemetry spliced/replaced the bucket at that index, after the tape was rebuilt from a different recording, or a checkpoint whose timeMs crosses a bucket boundary that later changed.

Common situations: Accepting corrected live telemetry that overwrote the bucket a checkpoint pointed at; loading checkpoints from a previous recording session; simulating the world independently and restoring into a PetWorld with a different tape history.

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

Appendix: source

Thrown at pet/ios/Resources/pet-native.js:386

        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;

View on GitHub (pinned to 433685b202)