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
- Convert time to samples explicitly with Math.round(seconds * sampleRate) before calling
- Clamp sampleRate to a supported integer value (8000-96000), typically the audio context's sampleRate rounded
- Chunk long renders into windows of at most sampleRate*120 samples per call
- 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
- Always convert seconds to samples with Math.round(seconds * sampleRate)
- Chunk renders to windows no longer than 120 seconds
- Round AudioContext sample rates to integers before passing them
- Clamp seek offsets to >= 0
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
- Invalid pet voice.
- Invalid pet PCM range.
- Invalid pet voice.
- Invalid pet voice.
- Model ' ' requires cloned voice data. Pass --clone-voice…
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)