{"record":{"id":"0e2e5fd82ebaa8e6","repo":"Hmbown/CodeWhale","slug":"pet-tape-exceeds-64-mib","errorCode":null,"errorMessage":"Pet tape exceeds 64 MiB.","messagePattern":"Pet tape exceeds 64 MiB\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":1115,"sourceCode":"const pet_sim_js_1 = require(\"./pet-sim.js\");\n/** One projection for imports, demos and recorded live snapshots. Times are ms.\n * These are aesthetic encodings of measured events, never model confidence. */\nexports.PET_BIN_MS = 400;\nfunction validatePetBucket(value) {\n    (0, pet_sim_js_1.validatePetState)(value);\n    const b = value;\n    if (!b || typeof b !== 'object' || b.version !== 1 || !Number.isSafeInteger(b.sequence) || b.sequence < 0 || b.sequence > pet_sim_js_1.PET_MAX_SECONDS * 2.5\n        || b.simTimeMs !== b.sequence * exports.PET_BIN_MS || b.durationMs !== exports.PET_BIN_MS\n        || !model_js_1.CATEGORIES.includes(b.channel) || typeof b.waiting !== 'boolean'\n        || !Array.isArray(b.agentIds) || b.agentIds.length > 250_000 || b.agentIds.some(id => typeof id !== 'string' || !id || id.length > 4096)\n        || !Number.isSafeInteger(b.errors) || b.errors < 0 || b.errors > 250_000\n        || !Array.isArray(b.onsets) || b.onsets.length !== 13 || b.onsets.some(n => !Number.isSafeInteger(n) || n < 0 || n > 250_000)\n        || !Array.isArray(b.activeMs) || b.activeMs.length !== 13 || b.activeMs.some(n => !Number.isFinite(n) || n < 0 || n > 100_000_000))\n        throw new Error('Invalid version 1 pet bucket.');\n}\nfunction decodePetJSONL(text) {\n    if (text.length > 64 * 1024 * 1024)\n        throw new Error('Pet tape exceeds 64 MiB.');\n    const rows = text.split(/\\r?\\n/).filter(line => line.trim()).map(line => JSON.parse(line));\n    if (rows.length > 216_000)\n        throw new Error('Pet tape exceeds 24 hours.');\n    return rows.map((row, i) => { validatePetBucket(row); if (row.sequence !== i)\n        throw new Error('Non-contiguous pet tape.'); return row; });\n}\n/** A live file must advance before its contents count as a new observation.\n * Existing bytes, duplicate samples and a restarted sequence establish a\n * baseline; they never replay an old onset or human request. Drivers supply a\n * bounded tail and reset this cursor after suspension or a new attachment. */\nclass PetLiveTape {\n    sequence;\n    reset() { this.sequence = undefined; }\n    readTail(text) {\n        if (!text) {\n            this.reset();\n            return;\n        }","sourceCodeStart":1097,"sourceCodeEnd":1133,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L1097-L1133","documentation":"decodePetJSONL enforces a hard size limit of 64 MiB on the raw pet tape text before parsing, to bound memory and parse cost when replaying a day's activity. A larger tape is rejected outright with 'Pet tape exceeds 64 MiB.'","triggerScenarios":"Calling decodePetJSONL(text) where text.length > 67108864 — e.g. loading a tape file that was never rotated/truncated, concatenating multiple day-tapes, or a runaway producer appending unbounded rows.","commonSituations":"Long-running sessions whose JSONL tape grew past the cap; a bug duplicating bucket rows; passing the wrong (combined) file to the decoder.","solutions":["Check the tape file size (ls -lh) and split or truncate it into chunks under 64 MiB before decoding.","Decode the tape day-by-day or in segments, decoding each chunk separately instead of one string.","Rotate the tape at write time (start a new file per 24h/session) so it never exceeds the cap.","If the tape is bloated by duplicate rows, deduplicate by sequence before decoding; note the separate 216,000-row (24h) cap also applies."],"exampleFix":"// before\nconst tape = fs.readFileSync(dayFiles[0] + ',' + dayFiles[1]);\npets.decodePetJSONL(tape.toString()); // 80 MiB combined\n// after\nfor (const f of [dayFile1, dayFile2]) {\n  const text = fs.readFileSync(f);\n  pets.decodePetJSONL(text); // each under 64 MiB\n}","handlingStrategy":"try-catch","validationCode":"const stats = fs.statSync(tapePath);\nif (stats.size > 64 * 1024 * 1024) throw new Error('split or rotate tape ' + tapePath + ' before decoding');","typeGuard":"null","tryCatchPattern":"try {\n  pets.decodePetJSONL(text);\n} catch (e) {\n  if (e.message === 'Pet tape exceeds 64 MiB.') decodeInChunks(splitTape(text));\n  else throw e;\n}","preventionTips":["Rotate tape files per day/session so no single file can pass 64 MiB.","Check file size before reading a tape into memory.","Decode in chunks rather than concatenating multiple tapes."],"tags":["limits","persistence","memory"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}