Hmbown/CodeWhale · error · Error

Pet tape exceeds 64 MiB.

Error message

Pet tape exceeds 64 MiB.

What it means

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.'

Solutions

  1. Check the tape file size (ls -lh) and split or truncate it into chunks under 64 MiB before decoding.
  2. Decode the tape day-by-day or in segments, decoding each chunk separately instead of one string.
  3. Rotate the tape at write time (start a new file per 24h/session) so it never exceeds the cap.
  4. If the tape is bloated by duplicate rows, deduplicate by sequence before decoding; note the separate 216,000-row (24h) cap also applies.

Example fix

// before
const tape = fs.readFileSync(dayFiles[0] + ',' + dayFiles[1]);
pets.decodePetJSONL(tape.toString()); // 80 MiB combined
// after
for (const f of [dayFile1, dayFile2]) {
  const text = fs.readFileSync(f);
  pets.decodePetJSONL(text); // each under 64 MiB
}
Defensive patterns

Strategy: try-catch

Validate before calling

const stats = fs.statSync(tapePath);
if (stats.size > 64 * 1024 * 1024) throw new Error('split or rotate tape ' + tapePath + ' before decoding');

Type guard

null

Try / catch

try {
  pets.decodePetJSONL(text);
} catch (e) {
  if (e.message === 'Pet tape exceeds 64 MiB.') decodeInChunks(splitTape(text));
  else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: Long-running sessions whose JSONL tape grew past the cap; a bug duplicating bucket rows; passing the wrong (combined) file to the decoder.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/0e2e5fd82ebaa8e6. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/pet_watch/pet-native.js:1115

const pet_sim_js_1 = require("./pet-sim.js");
/** One projection for imports, demos and recorded live snapshots. Times are ms.
 * These are aesthetic encodings of measured events, never model confidence. */
exports.PET_BIN_MS = 400;
function validatePetBucket(value) {
    (0, pet_sim_js_1.validatePetState)(value);
    const b = value;
    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
        || b.simTimeMs !== b.sequence * exports.PET_BIN_MS || b.durationMs !== exports.PET_BIN_MS
        || !model_js_1.CATEGORIES.includes(b.channel) || typeof b.waiting !== 'boolean'
        || !Array.isArray(b.agentIds) || b.agentIds.length > 250_000 || b.agentIds.some(id => typeof id !== 'string' || !id || id.length > 4096)
        || !Number.isSafeInteger(b.errors) || b.errors < 0 || b.errors > 250_000
        || !Array.isArray(b.onsets) || b.onsets.length !== 13 || b.onsets.some(n => !Number.isSafeInteger(n) || n < 0 || n > 250_000)
        || !Array.isArray(b.activeMs) || b.activeMs.length !== 13 || b.activeMs.some(n => !Number.isFinite(n) || n < 0 || n > 100_000_000))
        throw new Error('Invalid version 1 pet bucket.');
}
function decodePetJSONL(text) {
    if (text.length > 64 * 1024 * 1024)
        throw new Error('Pet tape exceeds 64 MiB.');
    const rows = text.split(/\r?\n/).filter(line => line.trim()).map(line => JSON.parse(line));
    if (rows.length > 216_000)
        throw new Error('Pet tape exceeds 24 hours.');
    return rows.map((row, i) => { validatePetBucket(row); if (row.sequence !== i)
        throw new Error('Non-contiguous pet tape.'); return row; });
}
/** A live file must advance before its contents count as a new observation.
 * Existing bytes, duplicate samples and a restarted sequence establish a
 * baseline; they never replay an old onset or human request. Drivers supply a
 * bounded tail and reset this cursor after suspension or a new attachment. */
class PetLiveTape {
    sequence;
    reset() { this.sequence = undefined; }
    readTail(text) {
        if (!text) {
            this.reset();
            return;
        }

View on GitHub (pinned to 73e0f67d83)