ai/nanoid · error · RangeError
Wrong ID size
Error message
Wrong ID size
What it means
nanoid's `random(bytes)` coerces the requested byte count with `bytes |= 0` and throws a RangeError when the result is negative. This guards against NaN/negative/non-numeric inputs (and `valueOf` abuse) reaching `Buffer.allocUnsafe`, which would otherwise throw an opaque Node error. It means the size argument you passed to `random()` is not a valid non-negative integer.
Source
Thrown at index.js:21
export { urlAlphabet }
// `crypto.getRandomValues` rejects requests over 65536 bytes,
// so bigger buffers are filled by chunks.
const GET_RANDOM_LIMIT = 65536
function fillRandom(buffer) {
let from = 0
while (from < buffer.length) {
let to = Math.min(from + GET_RANDOM_LIMIT, buffer.length)
crypto.getRandomValues(buffer.subarray(from, to))
from = to
}
}
export function random(bytes) {
// `|=` convert `bytes` to number to prevent `valueOf` abusing
bytes |= 0
if (bytes < 0) throw new RangeError('Wrong ID size')
// `random()` is used rarely and not in hot paths, so it makes
// a direct crypto call instead of using a byte pool.
let buffer = Buffer.allocUnsafe(bytes)
fillRandom(buffer)
return buffer
}
export function customRandom(alphabet, defaultSize, getRandom) {
// Random bytes are 0-255. `random % alphabet.length` can waste
// that entropy by making some symbols more likely.
//
// `safeByteCutoff` will be divided by `alphabet.length` without remainder
// fixing issue of broken distribution.
//
// Example: with 17 symbols, `safeByteCutoff` is 255.
// Bytes 0-254 preserve entropy evenly: each symbol gets 15 source bytes.
// Byte 255 would map to `0` again, making one symbol slightly more likely.
// So we reject 255.View on GitHub (pinned to 07a39d62d8)
Solutions
- Pass a non-negative integer to `random()`, e.g. `random(16)`.
- Clamp the value before calling: `bytes = Math.max(0, Math.trunc(Number(bytes)))`.
- Fix the upstream computation producing the negative value (e.g. guard subtraction results with Math.max(0, ...)).
- If the value comes from user input/config, validate it is a positive integer before passing it on.
Example fix
// before const bytes = maxLen - currentLen const buf = random(bytes) // throws if currentLen > maxLen // after const bytes = Math.max(0, maxLen - currentLen) const buf = random(bytes)
Defensive patterns
Strategy: validation
Validate before calling
function isValidSize(n) {
return Number.isInteger(Number(n)) && Number(n) >= 0
}
if (!isValidSize(bytes)) bytes = Math.max(0, Math.trunc(Number(bytes))) || 0
const buffer = random(bytes) Type guard
function isNonNegativeInt(v) {
return typeof v === 'number' && Number.isInteger(v) && v >= 0
} Try / catch
let buffer
try {
buffer = random(bytes)
} catch (e) {
if (e instanceof RangeError && e.message === 'Wrong ID size') {
buffer = random(16) // sane default
} else {
throw e
}
} Prevention
- Always pass literal positive integers to random().
- Clamp computed sizes with Math.max(0, Math.trunc(x)).
- Never pass objects with valueOf as sizes.
- Validate config/env-provided sizes at startup.
When it happens
Trigger: Calling `random(-5)` directly; passing a fractional or huge negative number; passing an object whose `valueOf` returns a negative number or NaN (NaN |= 0 becomes 0, but a negative-returning valueOf triggers the throw); passing `undefined`-derived negative config values; passing a negative result of arithmetic (e.g. `maxSize - currentSize` when it goes negative).
Common situations: Config values read from env/JSON where a default of -1 is used as a sentinel; computing ID length from a subtraction that underflows; deserializing user input that contains a negative number; test code seeding lengths with negative placeholders; passing a string like '-21' that gets coerced to a negative number.
AI-assisted analysis of ai/nanoid@07a39d62d8 (2026-08-30).
Data as JSON: /api/errors/212fa550ca924785.
Report an issue: GitHub.