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

  1. Convert before passing: Number.parseInt(value, 10) or Math.round(value)
  2. Coerce env vars once at config load: port: Number(process.env.PORT)
  3. 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

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


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)