Hmbown/CodeWhale · error · Error
Only a live recording can resume observation.
Error message
Only a live recording can resume observation.
What it means
resumeObservation() fast-forwards the simulation to just past the last accepted live telemetry packet so observation continues without replaying old audio or resurrecting stale requests. It requires the recording to be genuinely live: there must be at least one packet, and the playhead may lag the end by at most 24 ticks (800 ms). Anything else — an empty recording or one that has fallen too far behind — is not live, so resuming is refused.
Solutions
- Ensure acceptTelemetry has delivered at least one packet and the stream is still live before calling resumeObservation.
- If the recording is old or archived, replay/seek through step() instead of resumeObservation().
- Keep the playback loop ticking (call step() regularly) so tick stays within 24 ticks of the last packet.
- Check tick vs. last packet sequence before calling resume and reconnect/refresh telemetry if the lag is too large.
Example fix
// before
world.resumeObservation(); // throws if recording is stale or empty
// after
const last = world.tapeLog.at(-1), end = last ? (last.sequence + 1) * 12 : 0;
if (end && end - world.tick <= 24) world.resumeObservation();
else world.step((end - world.tick) / 30, { motion: false, sensitivity: 1 }); Defensive patterns
Strategy: type-guard
Validate before calling
function canResumeObservation(world) {
const last = world.tapeLog.at(-1);
const end = last ? (last.sequence + 1) * 12 : 0;
return end > 0 && end - world.tick <= 24;
} Type guard
const isLiveRecording = (world) => Array.isArray(world?.tapeLog) && world.tapeLog.length > 0 && (() => { const l = world.tapeLog.at(-1); return (l.sequence + 1) * 12 - world.tick <= 24; })(); Try / catch
try {
world.resumeObservation();
} catch (e) {
if (e.message === 'Only a live recording can resume observation.') {
// stale or empty recording: fast-forward manually or restart stream
world.step(remainingTicks / 30, { motion: false, sensitivity: 1 });
} else throw e;
} Prevention
- Only call resumeObservation on an actively-fed live stream
- Check tick lag vs. the last packet sequence before resuming
- Keep the playback loop stepping while telemetry flows
- Reconnect telemetry before resuming after a long suspension
When it happens
Trigger: Calling world.resumeObservation() when tapeLog is empty (end === 0), or when end - this.tick > 24, i.e. the sim tick is more than 24 ticks behind the last packet's end (sequence+1)*12.
Common situations: Calling resume after loading an archived/finished recording instead of a live one; a paused or suspended host that stopped accepting telemetry for more than ~0.8 s; calling resume before the first acceptTelemetry packet arrives; clock drift after a long block of the event loop.
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
- No pet segment was prepared.
- No pet segment was prepared.
- This Runtime already owns a task execution manager
- A cancelled Operation cannot be edited.
- Agent not found
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/986e4d666f8823ad.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:419
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() {
const last = this.tapeLog.at(-1), end = last ? (last.sequence + 1) * 12 : 0;
if (!end || end - this.tick > 24)
throw new Error('Only a live recording can resume observation.');
while (this.tick < end)
this.step(1 / 30, { motion: false, sensitivity: 1 });
this.voices = [];
}
/** Branch at the current playhead; input is journalled for the next fixed tick.
* A live touch never needs to re-simulate the creature's entire lifetime. */
interact(kind, x, y) {
if (!['attention', 'food'].includes(kind) || !Number.isFinite(x) || !Number.isFinite(y) || Math.abs(x) > 1 || Math.abs(y) > 1)
throw new Error('Invalid pet interaction.');
if (this.branchTick !== this.tick)
this.interactionLog.splice(this.interactionIndex);
this.branchTick = this.tick;
this.interactionLog.push({ timeMs: (this.tick + 1) * 1000 / HZ, kind, x, y });
}
/** Accept a live source packet at the next 400ms boundary. The accepted tape,
* including any missing intervals, is the exact replay authority for this host. */
acceptTelemetry(input) {
(0, pet_telemetry_js_1.validatePetBucket)(input);View on GitHub (pinned to 73e0f67d83)