denoland/deno · error · TypeError

ERR_INVALID_ARG_VALUE

ERR_INVALID_ARG_VALUE

Error message

The property 'options.${key}' is invalid. Received ${value}

What it means

getUIntOption() reads numeric constructor options such as authTagLength and requires value >>> 0 === value, i.e. a valid uint32. Negative numbers, fractional numbers, NaN, values above 4294967295, and non-number types all fail the check and throw ERR_INVALID_ARG_VALUE on 'options.<key>' (e.g. options.authTagLength). Used by both Cipheriv and Decipheriv option handling.

Source

Thrown at ext/node/polyfills/internal/crypto/cipher.ts:489

  }

  set lastChunkIsNonZero(value: boolean) {
    this.#lastChunkIsNonZero = value;
  }
}

function getBlockSize(cipher: string): number {
  if (StringPrototypeStartsWith(cipher, "des")) {
    return 8;
  }
  return 16;
}

function getUIntOption(options, key) {
  let value;
  if (options && (value = options[key]) != null) {
    if (value >>> 0 !== value) {
      throw new ERR_INVALID_ARG_VALUE(`options.${key}`, value);
    }
    return value;
  }
  return -1;
}

function Decipheriv(
  cipher: string,
  key: any,
  iv: any,
  options?: any,
) {
  if (!ObjectPrototypeIsPrototypeOf(Decipheriv.prototype, this)) {
    return new Decipheriv(cipher, key, iv, options);
  }

  const authTagLength = getUIntOption(options, "authTagLength");

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass a non-negative integer between 0 and 4294967295, e.g. authTagLength: 16.
  2. Sanitize before the call: Number.isInteger(x) && x >= 0 && x <= 0xFFFFFFFF.
  3. Type the options object (authTagLength?: number) so strings are caught at compile time.

Example fix

// before
const d = crypto.createDecipheriv('aes-128-gcm', key, iv, { authTagLength: cfg.tagLen }); // cfg.tagLen = "16"

// after
const tagLen = Number(cfg.tagLen);
if (!Number.isInteger(tagLen) || tagLen < 0) throw new Error('bad tagLen');
const d = crypto.createDecipheriv('aes-128-gcm', key, iv, { authTagLength: tagLen });
Defensive patterns

Strategy: validation

Validate before calling

function validateUIntOption(value: unknown, name: string): number {
  const n = typeof value === 'string' ? Number(value) : value;
  if (typeof n !== 'number' || !Number.isInteger(n) || n < 0 || n > 0xFFFFFFFF)
    throw new TypeError(`options.${name} must be a uint32, got ${String(value)}`);
  return n;
}
const opts = { authTagLength: validateUIntOption(cfg.authTagLength, 'authTagLength') };

Type guard

function isUInt32(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && v >= 0 && v <= 0xFFFFFFFF;
}

Prevention

When it happens

Trigger: createDecipheriv('aes-128-gcm', key, iv, { authTagLength: -1 }); { authTagLength: 4.5 }; { authTagLength: '16' } (string from config/JSON); authTagLength parsed from a protocol header without numeric conversion.

Common situations: Options loaded from JSON/YAML where numbers arrive as strings; tag lengths computed from arithmetic that can go negative or fractional; environment variables passed straight into options.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/933cc676394b6c5b. Report an issue: GitHub.