Hmbown/CodeWhale · error · Error

Pet archive numbering is exhausted; existing files were…

Error message

Pet archive numbering is exhausted; existing files were preserved.

What it means

Thrown while scanning the recording directory for existing `.segment-NNNNNN.jsonl` archives when a suffix encodes a segment number at or above Number.MAX_SAFE_INTEGER. The recorder refuses to keep incrementing numbering because it could no longer represent the next segment number safely, and aborts without touching existing files.

Solutions

  1. Find the offending file: `ls | grep -E 'segment-[0-9]{6,}\.jsonl$'` and inspect its numeric suffix.
  2. Rename or remove the file with the out-of-range number (it is not a real archive this recorder created).
  3. If it is genuine history, renumber segments into the safe range and update any references.
  4. Retry creating the recorder after cleanup.

Example fix

// before: bogus file in recording dir
pet-recording.segment-99999999999999999999.jsonl
// after: remove or renumber
mv pet-recording.segment-99999999999999999999.jsonl /tmp/quarantine/
Defensive patterns

Strategy: validation

Validate before calling

import { readdir } from 'node:fs/promises';
const dir = dirname(recordingPath);
const bad = (await readdir(dir)).filter(n => /^\.segment-|segment-[0-9]{7,}\.jsonl$/.test(n));
if (bad.some(n => Number(n.match(/segment-([0-9]+)\./)[1]) >= Number.MAX_SAFE_INTEGER)) {
  throw new Error(`clean up out-of-range segment files: ${bad.join(', ')}`);
}

Prevention

When it happens

Trigger: A directory containing a file named `<basename>.segment-9007199254740991.jsonl` or larger (7+ digits beyond the safe range) when the recorder initializes; also a manually created or corrupted segment file with an absurd number.

Common situations: Hand-crafted or test files that match the segment pattern with a huge numeric suffix; corrupted directory contents from a previous misbehaving tool.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at pet/scripts/lib/pet-recorder.mjs:91

        length += bytesRead;
      }
      if (length !== Number(held.size)) throw new Error('The previous pet recording changed while reading; it was preserved.');
      const text = new TextDecoder('utf-8', { fatal: true }).decode(contents.subarray(0, length));
      if (text && !text.endsWith('\n')) throw new Error('The previous pet recording has an incomplete final row; it was preserved.');
      decodePetJSONL(text);
      const unchanged = await output.stat({ bigint: true });
      if (unchanged.size !== original.size || unchanged.mtimeNs !== original.mtimeNs)
        throw new Error('The previous pet recording changed while reading; it was preserved.');
      bytes = length; restart = true; expectedMtime = original.mtimeNs;
      // Continue archive numbering without collecting a growing directory list.
      const prefix = `${basename(path)}.segment-`;
      for await (const entry of await opendir(dirname(path))) {
        if (!entry.name.startsWith(prefix)) continue;
        const suffix = entry.name.slice(prefix.length);
        if (!/^[0-9]{6,}\.jsonl$/.test(suffix)) continue;
        const number = Number(suffix.slice(0, -6));
        if (!Number.isSafeInteger(number) || number >= Number.MAX_SAFE_INTEGER)
          throw new Error('Pet archive numbering is exhausted; existing files were preserved.');
        segment = Math.max(segment, number);
      }
    }
    expectedMtime ??= (await output.stat({ bigint: true })).mtimeNs;
  } catch (error) { try { await output?.close(); } finally { lock.close(); } throw error; }
  return {
    async append(bucket) {
      if (!output || busy) throw new Error('Pet recorder is closed or already writing.');
      validatePetBucket(bucket);
      busy = true;
      try {
        await lock.check();
        const held = await output.stat({ bigint: true }), current = await lstat(path, { bigint: true });
        if (!current.isFile() || held.dev !== current.dev || held.ino !== current.ino || held.size !== BigInt(bytes) || held.mtimeNs !== expectedMtime)
          throw new Error('The live pet recording was changed or replaced externally; existing files were preserved.');
        const encode = seq => encodePetJSONL([{ ...bucket, sequence: seq, simTimeMs: seq * PET_BIN_MS }]);
        let row = encode(sequence), size = Buffer.byteLength(row);
        if (restart || sequence >= maxBuckets || bytes + size > maxBytes) {

View on GitHub (pinned to 433685b202)