{"record":{"id":"7a8b46423154272a","repo":"Hmbown/CodeWhale","slug":"live-pet-input-exceeds-its-tail-limit-telemetry","errorCode":null,"errorMessage":"Live pet input exceeds its tail limit.","messagePattern":"Live pet input exceeds its tail limit\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-telemetry.ts","lineNumber":49,"sourceCode":"}\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); }\n    catch (error) { this.reset(); throw error; }\n    const previous = this.sequence; this.sequence = packet.sequence;\n    if (previous === undefined || packet.sequence <= previous) return;\n    return packet;\n  }\n}\n\nconst order = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0;\nconst keyOf = (e: WhaleEvent) => JSON.stringify([e.traceId, e.id]);\nconst isContainer = (e: WhaleEvent) => e.attributes['whalesong.container'] === true\n  || e.attributes['codewhale.container'] === true;\n\n/** Compile a single trace. Unknown-duration spans provide onsets, not occupancy.","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-telemetry.ts#L31-L67","documentation":"Raised by readTail when a live pet input's tail exceeds the configured tail limit. The live tailing reader applies a bounded window on incoming text; a single append or accumulated tail larger than that limit cannot be processed incrementally, so the reader refuses the input rather than buffering unbounded data.","triggerScenarios":"Calling readTail with a text buffer longer than 256 KiB — e.g. the driver passed the whole accumulated file instead of a bounded tail, or the file grew without the cursor advancing.","commonSituations":"A driver bug reading the entire JSONL file on each poll instead of the last chunk; a watcher that never fired so the tail grew unbounded; attaching to a very large pre-existing file without resetting the cursor first.","solutions":["Pass only a bounded tail (≤256 KiB, typically the last line or last N bytes) to readTail","Call petLiveTape.reset() and re-attach starting from the file's current end","Track byte offsets in the driver and slice the tail per poll","Pre-truncate files larger than the limit before attaching"],"exampleFix":"// before\ntape.readTail(fs.readFileSync(livePath, 'utf8'));\n// after\nconst text = fs.readFileSync(livePath, 'utf8');\nconst tail = text.length > 262_144 ? text.slice(-262_144).slice(text.slice(-262_144).indexOf('\\n') + 1) : text;\ntape.readTail(tail);","handlingStrategy":"validation","validationCode":"function boundedTail(text: string): string {\n  if (text.length <= 262_144) return text;\n  const slice = text.slice(-262_144);\n  const nl = slice.indexOf('\\n');\n  return nl === -1 ? '' : slice.slice(nl + 1);\n}","typeGuard":"null","tryCatchPattern":"try {\n  const bucket = tape.readTail(tail);\n} catch (e) {\n  if (e.message === 'Live pet input exceeds its tail limit.') {\n    tape.reset(); // already reset internally; re-attach from file end\n    tail = readLastChunk(livePath, 64 * 1024);\n  } else throw e;\n}","preventionTips":["Track byte offsets in the driver and pass only new bytes per poll","Reset the cursor after suspension or re-attachment","Never feed a whole growing file to readTail"],"tags":["limits","streaming","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"}