{"record":{"id":"a84d654151e61259","repo":"Hmbown/CodeWhale","slug":"pet-tape-exceeds-24-hours-telemetry","errorCode":null,"errorMessage":"Pet tape exceeds 24 hours.","messagePattern":"Pet tape exceeds 24 hours\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-telemetry.ts","lineNumber":36,"sourceCode":"}\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;\n    let packet: unknown;\n    try { packet = JSON.parse(line); validatePetBucket(packet); }","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-telemetry.ts#L18-L54","documentation":"Thrown by decodePetJSONL when the tape contains more than 216,000 non-empty JSONL rows. 216,000 rows equals 24 hours at PET_BIN_MS bucket granularity, so this guard rejects a tape that would represent more than one day of telemetry and would break downstream bucket/sequence assumptions.","triggerScenarios":"Calling decodePetJSONL on a JSONL string that splits into more than 216,000 non-empty lines — e.g. a file accumulated over multiple days without rotation.","commonSituations":"Writer never rotates the daily tape; the loader was pointed at an aggregate/merged file; a bug duplicated rows so the count blew past the cap.","solutions":["Rotate the tape daily at the writer and decode one day at a time","Split the text into ≤216k-row chunks and decode each batch separately","Deduplicate rows before decoding if duplication inflated the count","Archive older days instead of concatenating them into one tape"],"exampleFix":"// before\nconst all = decodePetJSONL(weekOfTapes);\n// after\nconst days = weekOfTapes.split('\\n---DAY---\\n');\nconst all = days.flatMap(day => decodePetJSONL(day));","handlingStrategy":"validation","validationCode":"const rowCount = text.split(/\\r?\\n/).filter(l => l.trim()).length;\nif (rowCount > 216_000) throw new Error('Tape spans more than 24h; split before decoding');","typeGuard":"null","tryCatchPattern":"try {\n  buckets = decodePetJSONL(text);\n} catch (e) {\n  if (e.message === 'Pet tape exceeds 24 hours.') {\n    buckets = splitIntoDays(text).flatMap(decodePetJSONL);\n  } else throw e;\n}","preventionTips":["Rotate the tape at the writer every 24h / 216k bins","Deduplicate rows before decoding","Never concatenate multiple days into one decode call"],"tags":["limits","telemetry","duration"],"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-22T21:17:16.096Z"}