Hmbown/CodeWhale · error · Error

Pet tape exceeds 24 hours.

Error message

Pet tape exceeds 24 hours.

What it means

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.

Solutions

  1. Rotate the tape daily at the writer and decode one day at a time
  2. Split the text into ≤216k-row chunks and decode each batch separately
  3. Deduplicate rows before decoding if duplication inflated the count
  4. Archive older days instead of concatenating them into one tape

Example fix

// before
const all = decodePetJSONL(weekOfTapes);
// after
const days = weekOfTapes.split('\n---DAY---\n');
const all = days.flatMap(day => decodePetJSONL(day));
Defensive patterns

Strategy: validation

Validate before calling

const rowCount = text.split(/\r?\n/).filter(l => l.trim()).length;
if (rowCount > 216_000) throw new Error('Tape spans more than 24h; split before decoding');

Type guard

null

Try / catch

try {
  buckets = decodePetJSONL(text);
} catch (e) {
  if (e.message === 'Pet tape exceeds 24 hours.') {
    buckets = splitIntoDays(text).flatMap(decodePetJSONL);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/a84d654151e61259. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/pet-telemetry.ts:36

}

export function validatePetBucket(value: unknown): asserts value is PetBucket {
  validatePetState(value);
  const b = value as PetBucket;
  if (!b || typeof b !== 'object' || b.version !== 1 || !Number.isSafeInteger(b.sequence) || b.sequence < 0 || b.sequence > PET_MAX_SECONDS * 2.5
    || b.simTimeMs !== b.sequence * PET_BIN_MS || b.durationMs !== PET_BIN_MS
    || !CATEGORIES.includes(b.channel as Category) || typeof b.waiting !== 'boolean'
    || !Array.isArray(b.agentIds) || b.agentIds.length > 250_000 || b.agentIds.some(id => typeof id !== 'string' || !id || id.length > 4096)
    || !Number.isSafeInteger(b.errors) || b.errors < 0 || b.errors > 250_000
    || !Array.isArray(b.onsets) || b.onsets.length !== 13 || b.onsets.some(n => !Number.isSafeInteger(n) || n < 0 || n > 250_000)
    || !Array.isArray(b.activeMs) || b.activeMs.length !== 13 || b.activeMs.some(n => !Number.isFinite(n) || n < 0 || n > 100_000_000))
    throw new Error('Invalid version 1 pet bucket.');
}

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); }

View on GitHub (pinned to 433685b202)