Hmbown/CodeWhale · error · Error

Native habitat exceeds 8 MiB.

Error message

Native habitat exceeds 8 MiB.

What it means

restoreRecording(text) replaces the whole native habitat from a serialized recording and enforces an 8 MiB cap on the input string. Recordings can legitimately be large (full telemetry tape plus state), so the ceiling is generous, but anything beyond it is treated as runaway growth or a wrong payload and rejected before parsing.

Solutions

  1. Trim the recording: drop old telemetry buckets and keep only recent state before restoring
  2. Use recordingChunk()/prepareSegment()+commitSegment() to persist incrementally instead of one giant blob
  3. Compress the recording before storing and decompress within the 8 MiB budget at restore time
  4. Verify you are restoring a single recording, not a concatenated or duplicated blob

Example fix

// before
pet.restoreRecording(entireSessionDump);
// after
const trimmed = pruneOldBuckets(entireSessionDump, /* keepMs */ 10 * 60 * 1000);
if (trimmed.length > 8 * 1024 * 1024) throw new Error('recording still too large; use chunked persistence');
pet.restoreRecording(trimmed);
Defensive patterns

Strategy: validation

Validate before calling

function canRestoreRecording(text) { return typeof text === 'string' && text.length <= 8 * 1024 * 1024; }

Try / catch

try {
  pet.restoreRecording(text);
} catch (err) {
  if (err.message === 'Native habitat exceeds 8 MiB.') {
    pet.restoreRecording(compactRecording(text));
  } else throw err;
}

Prevention

When it happens

Trigger: Calling restoreRecording (directly or via restoreCheckpoint wrapping) with a string over 8*1024*1024 characters — typically an unbounded live tape that accumulated for a very long session, or concatenated multiple recordings.

Common situations: Very long-running live sessions whose JSONL tape grew past 8 MiB; accidental double-serialization of the same recording; archiving chunks into one giant string instead of using recordingChunk().

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/1a282cdfe2721355. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/pet-native.ts:62

  interactions(): string { return JSON.stringify(this.world.interactions); }
  accept(packet: string): void { this.world.acceptTelemetry(JSON.parse(packet)); }
  acceptLiveTail(text: string): boolean {
    const packet = this.liveTape.readTail(text); if (!packet) return false;
    this.world.acceptTelemetry(packet); return true;
  }
  resetLiveInput(): number { this.liveTape.reset(); return this.resumeEngine(); }
  recording(withCheckpoint = false): string { return JSON.stringify(this.world.recording(withCheckpoint)); }
  needsSegment(): boolean { return this.world.needsSegment; }
  prepareSegment(): string { this.segment = this.world.prepareSegment(); return JSON.stringify(this.segment.recording); }
  commitSegment(): void { if (!this.segment) throw new Error('No pet segment was prepared.'); this.segment.commit(); this.segment = undefined; }
  checkpoint(): string { return JSON.stringify(this.world.checkpoint()); }
  recordingChunk(index: number, completed = false): string | null { return this.world.recordingChunk(index, completed); }
  restoreCheckpoint(text: string): void {
    if (text.length > 512 * 1024) throw new Error('Pet checkpoint exceeds its size limit.');
    this.restoreRecording(JSON.stringify({ ...this.world.recording(false), checkpoint: JSON.parse(text) }));
  }
  restoreRecording(text: string): void {
    if (text.length > 8 * 1024 * 1024) throw new Error('Native habitat exceeds 8 MiB.');
    const points = this.world.sim.p.map(p => [p.hx, p.hy] as [number, number]);
    this.world = PetWorld.fromRecording(points, JSON.parse(text));
    this.liveTape.reset();
    this.segment = undefined; this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000); this.engine = new PetEngineTelemetry();
  }
  /** Resume a live host at the first unrecorded bucket. Keep every accepted
   * interval, but never present its last observed frame as current evidence. */
  resumeEngine(): number {
    this.world.resumeObservation();
    this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);
    this.engine = new PetEngineTelemetry();
    this.world.voices = [];
    return this.world.frame.timeMs;
  }
  observeEngine(metadataJSON: string, timeMs: number): void { this.engine.observe(JSON.parse(metadataJSON), timeMs); }
  observeEngineBatch(metadataJSON: string, timeMs: number): void {
    const events: unknown = JSON.parse(metadataJSON);
    if (!Array.isArray(events) || events.length > 64) throw new Error('Invalid Engine batch.');

View on GitHub (pinned to 433685b202)