Hmbown/CodeWhale · error · Error
Live pet input exceeds its tail limit.
Error message
Live pet input exceeds its tail limit.
What it means
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.
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
Example fix
// before
tape.readTail(fs.readFileSync(livePath, 'utf8'));
// after
const text = fs.readFileSync(livePath, 'utf8');
const tail = text.length > 262_144 ? text.slice(-262_144).slice(text.slice(-262_144).indexOf('\n') + 1) : text;
tape.readTail(tail); Defensive patterns
Strategy: validation
Validate before calling
function boundedTail(text: string): string {
if (text.length <= 262_144) return text;
const slice = text.slice(-262_144);
const nl = slice.indexOf('\n');
return nl === -1 ? '' : slice.slice(nl + 1);
} Type guard
null
Try / catch
try {
const bucket = tape.readTail(tail);
} catch (e) {
if (e.message === 'Live pet input exceeds its tail limit.') {
tape.reset(); // already reset internally; re-attach from file end
tail = readLastChunk(livePath, 64 * 1024);
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- Live pet input exceeds its tail limit.
- Pet input exceeds 250000 events.
- Pet input exceeds 250000 events.
- Pet tape exceeds 24 hours.
- Pet tape exceeds 24 hours.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/7a8b46423154272a.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/pet-telemetry.ts:49
}
export function decodePetJSONL(text: string): PetBucket[] {
if (text.length > 64 * 1024 * 1024) throw new Error('Pet tape exceeds 64 MiB.');
const rows = text.split(/\r?\n/).filter(line => line.trim()).map(line => JSON.parse(line) as unknown);
if (rows.length > 216_000) throw new Error('Pet tape exceeds 24 hours.');
return rows.map((row, i) => { validatePetBucket(row); if (row.sequence !== i) throw new Error('Non-contiguous pet tape.'); return row; });
}
/** A live file must advance before its contents count as a new observation.
* Existing bytes, duplicate samples and a restarted sequence establish a
* baseline; they never replay an old onset or human request. Drivers supply a
* bounded tail and reset this cursor after suspension or a new attachment. */
export class PetLiveTape {
private sequence: number | undefined;
reset(): void { this.sequence = undefined; }
readTail(text: string): PetBucket | undefined {
if (!text) { this.reset(); return; }
if (text.length > 262_144) { this.reset(); throw new Error('Live pet input exceeds its tail limit.'); }
if (!text.endsWith('\n')) return;
const line = text.trimEnd().split('\n').at(-1);
if (!line) return;
let packet: unknown;
try { packet = JSON.parse(line); validatePetBucket(packet); }
catch (error) { this.reset(); throw error; }
const previous = this.sequence; this.sequence = packet.sequence;
if (previous === undefined || packet.sequence <= previous) return;
return packet;
}
}
const order = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0;
const keyOf = (e: WhaleEvent) => JSON.stringify([e.traceId, e.id]);
const isContainer = (e: WhaleEvent) => e.attributes['whalesong.container'] === true
|| e.attributes['codewhale.container'] === true;
/** Compile a single trace. Unknown-duration spans provide onsets, not occupancy.View on GitHub (pinned to 433685b202)