denoland/deno · error · Error
must be 'an integer' but was
Error message
${name} must be 'an integer' but was ${value} What it means
validateIntegerRange(value, name, min, max) in ext/node/polyfills/_utils.ts is the shared validator for integer-typed options across node: polyfills (defaults are the signed 32-bit range). If value fails Number.isInteger it throws a plain Error "${name} must be 'an integer' but was ${value}" — note there is no error code, and the single quotes around 'an integer' are part of the literal message.
Solutions
- Convert before passing: Number.parseInt(value, 10) or Math.round(value)
- Coerce env vars once at config load: port: Number(process.env.PORT)
- Validate early with Number.isInteger(v) and fail with your own clear error message
Example fix
// before
configure({ timeout: process.env.TIMEOUT }); // '5000' string → throws
// after
const timeout = Number(process.env.TIMEOUT);
if (!Number.isInteger(timeout)) throw new TypeError('TIMEOUT must be an integer');
configure({ timeout }); Defensive patterns
Strategy: validation
Validate before calling
function toIntOption(value: unknown, name: string, min?: number, max?: number): number {
const n = typeof value === 'number' ? value : Number(value);
if (!Number.isInteger(n)) throw new TypeError(`${name} must be an integer, got ${value}`);
if (min !== undefined && n < min) throw new RangeError(`${name} must be >= ${min}`);
if (max !== undefined && n > max) throw new RangeError(`${name} must be <= ${max}`);
return n;
}
configure({ timeout: toIntOption(process.env.TIMEOUT, 'timeout') }); Type guard
function isIntegerInRange(v: unknown, min = -2147483648, max = 2147483647): v is number {
return typeof v === 'number' && Number.isInteger(v) && v >= min && v <= max;
} Try / catch
try {
configure({ timeout: value as number });
} catch (err) {
if (err instanceof Error && err.message.includes("must be 'an integer'")) {
configure({ timeout: Math.round(Number(value)) });
} else throw err;
} Prevention
- Convert env vars with Number() at the config boundary, never pass raw strings
- Round or clamp divided values (e.g. ms→s conversions) before use
- Type config fields as number and validate with Number.isInteger in a startup check
When it happens
Trigger: Passing a float (1.5), NaN, Infinity, a numeric string ('1024'), or a numeric string from process.env directly to any integer-validated polyfill option that routes through this helper.
Common situations: Env vars (always strings) passed without Number() conversion; arithmetic producing floats (timeout / 1000 when timeout is not a multiple); JSON config storing numbers as strings.
Related errors
- ERR_INVALID_ARG_TYPE
- Bad cluster.schedulingPolicy
- Cannot specify two IPv4 addresses
- Cannot specify two IPv6 addresses
- ERR_CRYPTO_ECDH_INVALID_FORMAT
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/5873f6313dc85732.
Report an issue: GitHub.
Appendix: source
Thrown at ext/node/polyfills/_utils.ts:113
);
}
function spliceOne(list: string[], index: number) {
for (; index + 1 < list.length; index++) {
list[index] = list[index + 1];
}
ArrayPrototypePop(list);
}
function validateIntegerRange(
value: number,
name: string,
min = -2147483648,
max = 2147483647,
) {
// The defaults for min and max correspond to the limits of 32-bit integers.
if (!NumberIsInteger(value)) {
throw new Error(`${name} must be 'an integer' but was ${value}`);
}
if (value < min || value > max) {
throw new Error(
`${name} must be >= ${min} && <= ${max}. Value was ${value}`,
);
}
}
type OptionalSpread<T> = T extends undefined ? []
: [T];
function once<T = undefined>(
callback: (...args: OptionalSpread<T>) => void,
) {
let called = false;
return function (this: unknown, ...args: OptionalSpread<T>) {
if (called) return;View on GitHub (pinned to 9ad36f7a2c)