{"record":{"id":"bd62b7701409553c","repo":"Hmbown/CodeWhale","slug":"pet-tape-exceeds-24-hours","errorCode":null,"errorMessage":"Pet tape exceeds 24 hours.","messagePattern":"Pet tape exceeds 24 hours\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":1118,"sourceCode":"exports.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        }\n        if (text.length > 262_144) {\n            this.reset();\n            throw new Error('Live pet input exceeds its tail limit.');","sourceCodeStart":1100,"sourceCodeEnd":1136,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L1100-L1136","documentation":"decodePetJSONL() parses a persisted 'pet tape' (JSONL of pet buckets) and enforces hard limits: the tape may cover at most 216,000 one-second buckets, i.e. 24 hours of data. If the decoded tape holds more rows than that, the parser refuses it rather than loading an oversized tape.","triggerScenarios":"Calling decodePetJSONL (directly or via the pet-watch tape loader) on a JSONL file whose non-empty line count exceeds 216,000 — e.g. a tape was appended to for more than 24 hours, or an earlier run wrote without rotation.","commonSituations":"Long-running pet-watch sessions that never rotate the tape file; clock/timezone changes inflating bucket counts; a merged or concatenated tape from several sessions.","solutions":["Rotate or truncate the tape file so it holds at most 216,000 rows (24 hours of 1-second buckets).","Split the tape into per-day files and decode each segment separately.","Raise the 216_000 cap only if 1-second bucketing assumptions genuinely changed — otherwise keep the limit.","Export the tape to a compacted/downsampled format before decoding."],"exampleFix":"// before\ndecodePetJSONL(fs.readFileSync('pet-tape.jsonl', 'utf8')); // throws on >216k rows\n// after\nconst rows = fs.readFileSync('pet-tape.jsonl', 'utf8').trim().split('\\n');\nconst last24h = rows.slice(-216_000).join('\\n');\ndecodePetJSONL(last24h);","handlingStrategy":"validation","validationCode":"function canDecodeTape(text) {\n  const rows = text.split(/\\r?\\n/).filter(l => l.trim());\n  return rows.length <= 216_000;\n}\nif (!canDecodeTape(raw)) raw = raw.split('\\n').slice(-216_000).join('\\n');","typeGuard":"function isWithinTapeCapacity(rows) {\n  return Array.isArray(rows) && rows.length <= 216_000;\n}","tryCatchPattern":"try {\n  const tape = decodePetJSONL(raw);\n} catch (e) {\n  if (e.message.includes('exceeds 24 hours')) {\n    const tail = raw.split(/\\r?\\n/).filter(Boolean).slice(-216_000).join('\\n');\n    const tape = decodePetJSONL(tail);\n  } else throw e;\n}","preventionTips":["Rotate the tape file daily (24h of 1-second buckets = 216,000 rows max).","Check row count with wc -l before decoding.","Never append across sessions into one tape file.","Downsample to coarser buckets for periods longer than 24 hours."],"tags":["input-validation","size-limit","jsonl"],"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-23T02:17:17.105Z"}