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
- Restore using the tape and interaction log that were captured together with the checkpoint (as in the original recording blob).
- Re-take the checkpoint (world.checkpoint(2)) against the current tape if the recording was legitimately modified.
- 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
- Always restore with the tape/interactions stored alongside the checkpoint.
- Never mutate a recording's tape after checkpointing.
- Keep recording blobs immutable once written.
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
- Pet checkpoint does not match its recording.
- Pet segment checkpoints do not agree.
- Pet checkpoint does not match its recording.
- Pet checkpoint telemetry does not match its clock.
- Pet checkpoint telemetry does not match its clock.
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, withoutView on GitHub (pinned to 433685b202)