Hmbown/CodeWhale · error · Error

Invalid pet PCM range.

Error message

Invalid pet PCM range.

What it means

renderPetPCM validates the sample window before synthesizing audio for the pet visualization. It throws 'Invalid pet PCM range.' when the sampleRate, startSample, or length arguments violate the accepted envelope: sampleRate must be an integer in [8000, 96000], startSample a non-negative safe integer, and length a non-negative integer no greater than 120 seconds worth of samples. This is an input-guard so the renderer never allocates unbounded or nonsensical buffers.

Solutions

  1. Convert time to samples explicitly with Math.round(seconds * sampleRate) before calling
  2. Clamp sampleRate to a supported integer value (8000-96000), typically the audio context's sampleRate rounded
  3. Chunk long renders into windows of at most sampleRate*120 samples per call
  4. Ensure startSample is a non-negative safe integer (use Math.max(0, Math.trunc(x)))

Example fix

// before
renderPetPCM(voices, elapsedMs, durationMs);
// after
const sr = 48000;
renderPetPCM(voices, Math.round(elapsedSec * sr), Math.min(Math.round(durationSec * sr), sr * 120), sr);
Defensive patterns

Strategy: validation

Validate before calling

function validPcmRange(startSample, length, sampleRate) {
  return Number.isInteger(sampleRate) && sampleRate >= 8000 && sampleRate <= 96000
    && Number.isSafeInteger(startSample) && startSample >= 0
    && Number.isInteger(length) && length >= 0 && length <= sampleRate * 120;
}
if (!validPcmRange(start, len, sr)) throw new RangeError('pcm range');

Type guard

const isPcmWindow = (s) => typeof s === 'object' && Number.isSafeInteger(s.startSample) && s.startSample >= 0 && Number.isInteger(s.length) && s.length >= 0 && Number.isInteger(s.sampleRate) && s.sampleRate >= 8000 && s.sampleRate <= 96000;

Try / catch

try {
  const pcm = renderPetPCM(voices, startSample, length, sampleRate);
} catch (err) {
  if (err.message === 'Invalid pet PCM range.') {
    startSample = Math.max(0, Math.trunc(startSample));
    length = Math.min(Math.max(0, Math.trunc(length)), sampleRate * 120);
    return renderPetPCM(voices, startSample, length, sampleRate);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling renderPetPCM with a non-integer or out-of-range sampleRate (e.g. 44100.5, 4000, 192000), a negative or non-integer startSample (NaN, -1, fractional seek offsets), or a length that is negative, non-integer, or exceeds sampleRate*120 samples.

Common situations: Passing a duration in milliseconds directly as a sample count without multiplying by sampleRate; using a fractional sampleRate from an AudioContext probe; computing startSample from floating-point time math without Math.round/floor; rendering an entire long session at once instead of chunked windows.

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@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/3b49b68e0630bf50. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/pet_watch/pet-native.js:1624

                if (frame.needs === 'call' && window % 10 === 0)
                    add(`call:${window}`, pet_sim_js_1.CHANNELS[11].freq * 1.5, .38, .03);
            }
        }
        return out;
    }
}
exports.PetScore = PetScore;
/** Sample-addressed noise: no global RNG, identical samples when chunked/seeking. */
function noise(seed, sample) {
    let x = (seed + Math.imul(sample, 0x6D2B79F5)) >>> 0;
    x = Math.imul(x ^ x >>> 15, x | 1);
    x ^= x + Math.imul(x ^ x >>> 7, x | 61);
    return ((x ^ x >>> 14) >>> 0) / 2147483648 - 1;
}
function renderPetPCM(voices, startSample, length, sampleRate = 48_000) {
    if (!Number.isInteger(sampleRate) || sampleRate < 8000 || sampleRate > 96000
        || !Number.isSafeInteger(startSample) || startSample < 0 || !Number.isInteger(length) || length < 0 || length > sampleRate * 120)
        throw new Error('Invalid pet PCM range.');
    const left = new Float32Array(length), right = new Float32Array(length);
    for (const v of voices) {
        if (![v.start, v.duration, v.frequency, v.gain, v.pan].every(Number.isFinite)
            || v.start < 0 || v.duration <= 0 || v.duration > 10 || v.frequency <= 0 || v.frequency > sampleRate / 2
            || v.gain < 0 || v.gain > 1 || Math.abs(v.pan) > 1 || !['tone', 'noise'].includes(v.kind))
            throw new Error('Invalid pet voice.');
        const first = Math.max(startSample, Math.ceil(v.start * sampleRate));
        const last = Math.min(startSample + length, Math.ceil((v.start + v.duration) * sampleRate));
        const pan = (v.pan + 1) * Math.PI / 4, seed = (0xC0FFEE ^ (0, model_js_1.stableHash)(v.id)) >>> 0;
        for (let absolute = first; absolute < last; absolute++) {
            const age = absolute / sampleRate - v.start;
            const envelope = Math.min(1, age / .015, (v.duration - age) / .045);
            const sample = v.kind === 'noise' ? noise(seed, absolute) * Math.exp(-age * 14)
                : Math.sin(2 * Math.PI * v.frequency * age) * .88 + Math.sin(4 * Math.PI * v.frequency * age) * .12;
            const value = sample * Math.max(0, envelope) * v.gain, at = absolute - startSample;
            left[at] += value * Math.cos(pan);
            right[at] += value * Math.sin(pan);
        }

View on GitHub (pinned to 73e0f67d83)