Hmbown/CodeWhale · error · Error
The pet recording segment has changed.
Error message
The pet recording segment has changed.
What it means
prepareSegment returns a commit() closure that splices consumed input off the live world's logs. Before mutating, it re-checks that this.origin is still the checkpoint it captured and that the world has not ticked backwards past the segment boundary (this.tick < c.tick). If the world moved on — the origin advanced or ticks were consumed elsewhere — the segment being committed no longer matches the live state, so committing would corrupt the tape and the error is thrown instead.
Solutions
- Call commit() exactly once, immediately after prepareSegment(), before any further ticks.
- Discard the stale closure and call prepareSegment() again to get a fresh commit handle.
- Ensure only one code path owns the world and performs segment rotation.
- Do not restore/rollback the world between prepareSegment() and commit().
Example fix
// before const seg = world.prepareSegment(); world.tick(); // state advanced seg.commit(); // throws // after const seg = world.prepareSegment(); seg.commit(); // commit before any further ticks world.tick();
Defensive patterns
Strategy: try-catch
Validate before calling
// Track segment rotation on one owner:
if (pendingCommit) throw new Error('Previous segment commit not finished'); Try / catch
const seg = world.prepareSegment();
try {
seg.commit();
} catch (err) {
if (err.message === 'The pet recording segment has changed.') {
seg = null;
const fresh = world.prepareSegment(); fresh.commit(); // retry once with fresh state
} else throw err;
} Prevention
- Call commit() synchronously right after prepareSegment(), before any tick.
- Never invoke a prepared commit twice; null it out after use.
- Funnel all world mutation and segment rotation through one owner/path.
- Do not restore or roll back the world between prepareSegment and commit.
When it happens
Trigger: Calling the `commit()` returned by `prepareSegment()` after the world state changed between prepareSegment and commit: another commit already ran (committed flag), the origin checkpoint was replaced (e.g. by an earlier commit or restore), or the live world's tick dropped below the prepared checkpoint's tick.
Common situations: Calling commit() twice on the same prepared segment; interleaving prepareSegment/commit with a restore-from-checkpoint; driving the world from two code paths where one rolls it back; async capture of the commit closure invoked late after ticks/origin changed.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- The pet recording segment has changed.
- A cancelled Operation cannot be edited.
- Android loaded-image identity changed: /proc/self/maps has…
- Android loaded-image proof changed from
- Archive this pet recording before accepting more telemetry.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/854b1a592876110e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:288
/** Retire only consumed input. The exact origin makes each archived segment
* independently replayable; no particle, random stream or score is reset. */
prepareSegment() {
const dropTape = Math.max(0, this.bucketIndex), dropInputs = this.interactionIndex, previous = this.origin;
const tape = this.tapeLog.slice(dropTape), interactions = this.interactionLog.slice(dropInputs);
const points = this.sim.p.map(p => [p.hx, p.hy]);
const c = this.checkpoint();
c.petCheckpointVersion = 2;
c.historyStart = this.tick;
c.hasTelemetry = this.hasTelemetry;
c.bucketIndex -= dropTape;
c.interactionIndex = 0;
c.history = new PetWorld(points, tape, interactions, this.sim.expressionVersion, true).historyDigest();
const next = PetWorld.restore(points, tape, interactions, c);
next.origin = next.checkpoint();
let committed = false;
return { recording: next.recording(), archive: this.recording(true, true), commit: () => {
if (committed || this.origin !== previous || this.tick < c.tick)
throw new Error('The pet recording segment has changed.');
this.tapeLog.splice(0, dropTape);
this.interactionLog.splice(0, dropInputs);
this.bucketIndex -= dropTape;
this.interactionIndex -= dropInputs;
this.tapeHashes = [];
this.hashTape(0);
this.segmented = true;
this.origin = next.origin;
committed = true;
} };
}
/** Lossless version 1 export, including the current pose and score. Drivers
* consume all chunks synchronously on the world's owner before another tick.
* Only a small slice is serialized inside an embedded runtime at a time. */
recordingChunk(index, completed = false) {
if (!Number.isSafeInteger(index) || index < 0)
throw new Error('Invalid pet export cursor.');
const size = 16, tapeEnd = completed ? this.bucketIndex + 1 : this.tapeLog.length;View on GitHub (pinned to 73e0f67d83)