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
- Use checkpoints emitted by the library itself; do not edit frame.timeMs or frame.telemetry manually.
- Re-take the checkpoint from a live world so telemetry and time are regenerated together.
- 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
- Never edit frame.timeMs or frame.telemetry in a checkpoint.
- Use world.checkpoint(2) to generate checkpoints, never manual construction.
- Migrate checkpoints with library tooling, not ad-hoc field edits.
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
- Pet checkpoint telemetry does not match its clock.
- Pet checkpoint does not match its recording.
- Pet checkpoint does not match its recording.
- Pet checkpoint telemetry does not match its clock.
- Pet segment checkpoints do not agree.
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)