Hmbown/CodeWhale · error · Error

Pet tape exceeds 64 MiB.

Error message

Pet tape exceeds 64 MiB.

What it means

decodePetJSONL enforces a hard 64 MiB limit on the raw tape text before parsing. The limit protects the iOS/JS bridge from unbounded memory use when a pet tape file grows beyond any legitimate 24-hour window. Files larger than the cap are rejected before any JSON parsing occurs.

Solutions

  1. Rotate or truncate the tape to a single <=24h window (max 216,000 rows) before loading.
  2. Split the file and decode only the most recent segment.
  3. Fix the producer to stop appending past one day's worth of bins.
  4. Pre-check file size in the caller and surface a friendly rotation message.

Example fix

// before
const tape = await readFile(path, 'utf8'); const rows = decodePetJSONL(tape);
// after
const stat = await statFile(path); if (stat.size > 64 * 1024 * 1024) await rotateTape(path);
const rows = decodePetJSONL(await readFile(path, 'utf8'));
Defensive patterns

Strategy: validation

Validate before calling

if (tapeText.length > 64 * 1024 * 1024) { rotateTape(); } else { decodePetJSONL(tapeText); }

Type guard

const isDecodableTapeSize = (text) => typeof text === 'string' && text.length <= 64 * 1024 * 1024;

Try / catch

try { rows = decodePetJSONL(text); }
catch (e) { if (e.message === 'Pet tape exceeds 64 MiB.') { rotateTape(); rows = decodePetJSONL(readTail(path)); } else throw e; }

Prevention

When it happens

Trigger: Calling decodePetJSONL with a string whose .length exceeds 67,108,864 characters — typically a tape file that accumulated rows for multiple days, was concatenated/append-only without rotation, or whose producer wrote far more buckets than the 216,000-row maximum.

Common situations: A long-running daemon appending to the same pet tape forever; a log-rotation misconfiguration; a developer pointing the loader at a combined/aggregated tape of several sessions.

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/08a336c22ce09c78. Report an issue: GitHub.

Appendix: source

Thrown at pet/ios/Resources/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 433685b202)