{"record":{"id":"1a282cdfe2721355","repo":"Hmbown/CodeWhale","slug":"native-habitat-exceeds-8-mib-1a282c","errorCode":null,"errorMessage":"Native habitat exceeds 8 MiB.","messagePattern":"Native habitat exceeds 8 MiB\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-native.ts","lineNumber":62,"sourceCode":"  interactions(): string { return JSON.stringify(this.world.interactions); }\n  accept(packet: string): void { this.world.acceptTelemetry(JSON.parse(packet)); }\n  acceptLiveTail(text: string): boolean {\n    const packet = this.liveTape.readTail(text); if (!packet) return false;\n    this.world.acceptTelemetry(packet); return true;\n  }\n  resetLiveInput(): number { this.liveTape.reset(); return this.resumeEngine(); }\n  recording(withCheckpoint = false): string { return JSON.stringify(this.world.recording(withCheckpoint)); }\n  needsSegment(): boolean { return this.world.needsSegment; }\n  prepareSegment(): string { this.segment = this.world.prepareSegment(); return JSON.stringify(this.segment.recording); }\n  commitSegment(): void { if (!this.segment) throw new Error('No pet segment was prepared.'); this.segment.commit(); this.segment = undefined; }\n  checkpoint(): string { return JSON.stringify(this.world.checkpoint()); }\n  recordingChunk(index: number, completed = false): string | null { return this.world.recordingChunk(index, completed); }\n  restoreCheckpoint(text: string): void {\n    if (text.length > 512 * 1024) throw new Error('Pet checkpoint exceeds its size limit.');\n    this.restoreRecording(JSON.stringify({ ...this.world.recording(false), checkpoint: JSON.parse(text) }));\n  }\n  restoreRecording(text: string): void {\n    if (text.length > 8 * 1024 * 1024) throw new Error('Native habitat exceeds 8 MiB.');\n    const points = this.world.sim.p.map(p => [p.hx, p.hy] as [number, number]);\n    this.world = PetWorld.fromRecording(points, JSON.parse(text));\n    this.liveTape.reset();\n    this.segment = undefined; this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000); this.engine = new PetEngineTelemetry();\n  }\n  /** Resume a live host at the first unrecorded bucket. Keep every accepted\n   * interval, but never present its last observed frame as current evidence. */\n  resumeEngine(): number {\n    this.world.resumeObservation();\n    this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);\n    this.engine = new PetEngineTelemetry();\n    this.world.voices = [];\n    return this.world.frame.timeMs;\n  }\n  observeEngine(metadataJSON: string, timeMs: number): void { this.engine.observe(JSON.parse(metadataJSON), timeMs); }\n  observeEngineBatch(metadataJSON: string, timeMs: number): void {\n    const events: unknown = JSON.parse(metadataJSON);\n    if (!Array.isArray(events) || events.length > 64) throw new Error('Invalid Engine batch.');","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-native.ts#L44-L80","documentation":"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.","triggerScenarios":"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.","commonSituations":"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().","solutions":["Trim the recording: drop old telemetry buckets and keep only recent state before restoring","Use recordingChunk()/prepareSegment()+commitSegment() to persist incrementally instead of one giant blob","Compress the recording before storing and decompress within the 8 MiB budget at restore time","Verify you are restoring a single recording, not a concatenated or duplicated blob"],"exampleFix":"// before\npet.restoreRecording(entireSessionDump);\n// after\nconst trimmed = pruneOldBuckets(entireSessionDump, /* keepMs */ 10 * 60 * 1000);\nif (trimmed.length > 8 * 1024 * 1024) throw new Error('recording still too large; use chunked persistence');\npet.restoreRecording(trimmed);","handlingStrategy":"validation","validationCode":"function canRestoreRecording(text) { return typeof text === 'string' && text.length <= 8 * 1024 * 1024; }","typeGuard":null,"tryCatchPattern":"try {\n  pet.restoreRecording(text);\n} catch (err) {\n  if (err.message === 'Native habitat exceeds 8 MiB.') {\n    pet.restoreRecording(compactRecording(text));\n  } else throw err;\n}","preventionTips":["Persist incrementally with prepareSegment/commitSegment instead of one giant blob","Cap live-session tape length with periodic pruning","Compress recordings at rest and stay within the 8 MiB budget after decompression"],"tags":["recording","size-limit","restore"],"backgroundTag":"payload-too-large","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}