{"record":{"id":"8384854b91f1a150","repo":"Hmbown/CodeWhale","slug":"pet-tape-exceeds-64-mib-telemetry","errorCode":null,"errorMessage":"Pet tape exceeds 64 MiB.","messagePattern":"Pet tape exceeds 64 MiB\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-telemetry.ts","lineNumber":34,"sourceCode":"  agentIds: string[];\n  waiting: boolean;\n}\n\nexport function validatePetBucket(value: unknown): asserts value is PetBucket {\n  validatePetState(value);\n  const b = value as PetBucket;\n  if (!b || typeof b !== 'object' || b.version !== 1 || !Number.isSafeInteger(b.sequence) || b.sequence < 0 || b.sequence > PET_MAX_SECONDS * 2.5\n    || b.simTimeMs !== b.sequence * PET_BIN_MS || b.durationMs !== PET_BIN_MS\n    || !CATEGORIES.includes(b.channel as Category) || 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}\n\nexport function decodePetJSONL(text: string): PetBucket[] {\n  if (text.length > 64 * 1024 * 1024) throw new Error('Pet tape exceeds 64 MiB.');\n  const rows = text.split(/\\r?\\n/).filter(line => line.trim()).map(line => JSON.parse(line) as unknown);\n  if (rows.length > 216_000) throw new Error('Pet tape exceeds 24 hours.');\n  return rows.map((row, i) => { validatePetBucket(row); if (row.sequence !== i) throw new Error('Non-contiguous pet tape.'); return row; });\n}\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. */\nexport class PetLiveTape {\n  private sequence: number | undefined;\n  reset(): void { this.sequence = undefined; }\n  readTail(text: string): PetBucket | undefined {\n    if (!text) { this.reset(); return; }\n    if (text.length > 262_144) { this.reset(); throw new Error('Live pet input exceeds its tail limit.'); }\n    if (!text.endsWith('\\n')) return;\n    const line = text.trimEnd().split('\\n').at(-1);\n    if (!line) return;","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-telemetry.ts#L16-L52","documentation":"Thrown by decodePetJSONL before parsing when the raw JSONL text is larger than 64 MiB (64 * 1024 * 1024 chars). It is an input-size guard: the caller passed a pet tape file whose byte/char length exceeds the accepted ceiling, so decoding is refused up front instead of attempting to split and parse an oversized payload.","triggerScenarios":"Calling decodePetJSONL with a tape string whose .length exceeds 67,108,864 characters — e.g. reading an entire multi-day telemetry file into memory.","commonSituations":"A telemetry file grew past 24h/64MiB because the pet writer never rotated; a user pointed the loader at the wrong (much larger) log; concatenating many days of tapes before decoding.","solutions":["Split the tape into ≤64 MiB chunks and decode each with decodePetJSONL separately","Rotate or truncate the telemetry file at the writer so a single tape stays under the cap","Stream/parse line-by-line yourself and call validatePetBucket per row instead of decodePetJSONL","Delete or archive old tape files"],"exampleFix":"// before\nconst buckets = decodePetJSONL(fs.readFileSync(tapePath, 'utf8'));\n// after\nconst text = fs.readFileSync(tapePath, 'utf8');\nconst buckets = text.length <= 64*1024*1024 ? decodePetJSONL(text)\n  : text.split('\\n').reduce((acc, line) => acc.concat(decodePetJSONL(line + '\\n')), []);","handlingStrategy":"try-catch","validationCode":"if (text.length > 64 * 1024 * 1024) {\n  // split into chunks before calling decodePetJSONL\n}\nconst ok = text.length <= 64 * 1024 * 1024;","typeGuard":"null","tryCatchPattern":"try {\n  buckets = decodePetJSONL(text);\n} catch (e) {\n  if (e.message === 'Pet tape exceeds 64 MiB.') {\n    buckets = chunkText(text, 16 * 1024 * 1024).flatMap(decodePetJSONL);\n  } else throw e;\n}","preventionTips":["Rotate tape files daily or at a size threshold well below 64 MiB","Check file size before reading it fully into memory","Stream large tapes instead of bulk-reading"],"tags":["limits","memory","telemetry"],"backgroundTag":"file-size-limit-exceeded","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"}