{"record":{"id":"08a336c22ce09c78","repo":"Hmbown/CodeWhale","slug":"pet-tape-exceeds-64-mib-native","errorCode":null,"errorMessage":"Pet tape exceeds 64 MiB.","messagePattern":"Pet tape exceeds 64 MiB\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/ios/Resources/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/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L1097-L1133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Rotate or truncate the tape to a single <=24h window (max 216,000 rows) before loading.","Split the file and decode only the most recent segment.","Fix the producer to stop appending past one day's worth of bins.","Pre-check file size in the caller and surface a friendly rotation message."],"exampleFix":"// before\nconst tape = await readFile(path, 'utf8'); const rows = decodePetJSONL(tape);\n// after\nconst stat = await statFile(path); if (stat.size > 64 * 1024 * 1024) await rotateTape(path);\nconst rows = decodePetJSONL(await readFile(path, 'utf8'));","handlingStrategy":"validation","validationCode":"if (tapeText.length > 64 * 1024 * 1024) { rotateTape(); } else { decodePetJSONL(tapeText); }","typeGuard":"const isDecodableTapeSize = (text) => typeof text === 'string' && text.length <= 64 * 1024 * 1024;","tryCatchPattern":"try { rows = decodePetJSONL(text); }\ncatch (e) { if (e.message === 'Pet tape exceeds 64 MiB.') { rotateTape(); rows = decodePetJSONL(readTail(path)); } else throw e; }","preventionTips":["Rotate the tape daily; never append across days.","Stat the file size before loading and rotate proactively.","Cap producer output to one 24h window.","Test your rotation logic with an oversized fixture."],"tags":["limits","file-size","memory"],"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-22T16:17:23.217Z"}