Hmbown/CodeWhale · error

Invalid pet recording segment limit.

Error message

Invalid pet recording segment limit.

What it means

createPetRecorder validates the caller-supplied segment limits before touching the filesystem: maxBuckets must be a safe integer in [1, 216000] and maxBytes must be a safe integer in [1, 64 MiB]. Any value outside those ranges (or a non-integer like NaN, Infinity, a float, or a string) throws this error immediately, because an unbounded or invalid limit would recreate the unbounded append-only output the recorder is designed to replace.

Solutions

  1. Pass only safe integers: maxBuckets between 1 and 216000 and maxBytes between 1 and 67108864, or omit both to use the defaults (216000 buckets, 64 MiB).
  2. If limits come from env/config, coerce with Number() and validate with Number.isSafeInteger plus range checks before calling createPetRecorder.
  3. Log the actual computed values just before the call to spot float or string leakage; round or floor derived byte values.

Example fix

// before
await createPetRecorder(path, { maxBytes: opts.maxBytes ?? '64MB' });
// after
const maxBytes = Number(opts.maxBytes ?? 64 * 1024 * 1024);
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > 64 * 1024 * 1024) throw new Error(`bad maxBytes: ${opts.maxBytes}`);
await createPetRecorder(path, { maxBytes });
Defensive patterns

Strategy: validation

Validate before calling

function validLimits({ maxBuckets = 216000, maxBytes = 64 * 1024 * 1024 } = {}) {
  return Number.isSafeInteger(maxBuckets) && maxBuckets >= 1 && maxBuckets <= 216000
    && Number.isSafeInteger(maxBytes) && maxBytes >= 1 && maxBytes <= 64 * 1024 * 1024;
}

Type guard

const isSafeIntInRange = (v, lo, hi) => Number.isSafeInteger(v) && v >= lo && v <= hi;

Try / catch

try { await createPetRecorder(p, opts); } catch (e) { if (e.message === 'Invalid pet recording segment limit.') { console.error('bad limits', opts); process.exitCode = 2; } else throw e; }

Prevention

When it happens

Trigger: Calling createPetRecorder(path, { maxBuckets: 0 }), a negative maxBytes, maxBuckets: 216001 (above cap), maxBytes: 64*1024*1024 + 1 (above 64 MiB cap), or non-safe-integers such as NaN, Infinity, 1000.5, or a numeric string parsed from CLI args/env without coercion.

Common situations: Config values read from environment variables or JSON left as strings instead of numbers; a caller passing 0 or undefined-typed placeholders to 'disable' rotation; copy-pasting the 64*1024*1024 default with a `+1` headroom tweak; computing limits with float arithmetic (e.g. bytes/1024/1024).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

  };
  try {
    database = new DatabaseSync(name);
    database.exec('PRAGMA busy_timeout = 0; BEGIN EXCLUSIVE');
    await check();
    return { check, close: () => { database.close(); } };
  } catch (error) {
    database?.close();
    if (error.errcode === 5 || error.errcode === 6) throw new Error('Another pet recorder is using this output.');
    throw error;
  }
}

/** Replaces the CLI's unbounded append-only output. Each complete segment is
 * replayable on its own; the same live pathname always holds the newest one. */
export async function createPetRecorder(path, { maxBuckets = 216_000, maxBytes = 64 * 1024 * 1024, report = () => {}, resume = false } = {}) {
  if (!Number.isSafeInteger(maxBuckets) || maxBuckets < 1 || maxBuckets > 216_000
    || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > 64 * 1024 * 1024)
    throw new Error('Invalid pet recording segment limit.');
  path = resolve(await realpath(dirname(resolve(path))), basename(path));
  let lock = await lockRecorder(path), output, sequence = 0, bytes = 0, segment = 0, busy = false, restart = false, expectedMtime;
  try {
    try { output = await open(path, 'wx', 0o600); }
    catch (error) {
      if (!resume || error.code !== 'EEXIST') throw error;
      const original = await lstat(path, { bigint: true });
      if (!original.isFile() || original.size > 64n * 1024n * 1024n)
        throw new Error('The previous pet recording is not a bounded regular file; it was preserved.');
      output = await open(path, constants.O_RDWR | constants.O_APPEND | constants.O_NOFOLLOW | constants.O_NONBLOCK);
      const held = await output.stat({ bigint: true });
      if (held.dev !== original.dev || held.ino !== original.ino || held.size !== original.size)
        throw new Error('The previous pet recording changed while opening; it was preserved.');
      // Read at most the size already checked, including a single growth byte.
      const contents = Buffer.alloc(Number(held.size) + 1);
      let length = 0;
      while (length < contents.length) {
        const { bytesRead } = await output.read(contents, length, contents.length - length, length);

View on GitHub (pinned to 433685b202)