denoland/deno · error · TypeError

Invalid authentication tag length: ${tagByteLength}

Error message

Invalid authentication tag length: ${tagByteLength}

What it means

For GCM, when createDecipheriv() received no explicit authTagLength option (_authTagLength === -1), the authentication tag must be exactly 16 bytes (128 bits); any other tagByteLength throws TypeError 'Invalid authentication tag length: N'. This was Node deprecation DEP0182 and is now a hard error — the polyfill matches Node's current behavior. Shorter tags (4-15 bytes) are legal only when authTagLength was declared at construction.

Source

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

Decipheriv.prototype.setAuthTag = function (
  buffer: any,
  _encoding?: string,
) {
  if (this._authTag) {
    throw new ERR_CRYPTO_INVALID_STATE("setAuthTag");
  }
  // When no explicit `authTagLength` was given at decipher creation time, a
  // GCM authentication tag must be the full 128 bits (16 bytes); shorter tags
  // are only accepted when `authTagLength` is set. This used to be the DEP0182
  // deprecation warning and is now a hard error (matching Node.js).
  // deno-lint-ignore deno-internal/prefer-primordials -- `buffer` may be Buffer/TypedArray/DataView
  const tagByteLength = buffer.byteLength;
  if (
    this._isGcmMode && this._authTagLength === -1 &&
    tagByteLength !== 16
  ) {
    throw new TypeError(
      `Invalid authentication tag length: ${tagByteLength}`,
    );
  }
  // deno-lint-ignore deno-internal/prefer-primordials -- `buffer` may be Buffer/TypedArray/DataView
  op_node_decipheriv_auth_tag(this._context, buffer.byteLength);
  this._authTag = buffer;
  return this;
};

Decipheriv.prototype.setAutoPadding = function (autoPadding?: boolean) {
  this._autoPadding = Boolean(autoPadding);
  this._cache.lastChunkIsNonZero = this._autoPadding;
  return this;
};

Decipheriv.prototype.update = function (
  data: string | Buffer | ArrayBufferView,
  inputEncoding?: any,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Declare the expected length at construction: createDecipheriv('aes-128-gcm', key, iv, { authTagLength: tag.length }).
  2. Or make the encrypting side emit full 16-byte tags.
  3. Verify you are passing the actual tag: check slice offsets when splitting payload|tag, and assert tag.length matches the protocol.

Example fix

// before
const d = crypto.createDecipheriv('aes-128-gcm', key, iv);
d.setAuthTag(blob.subarray(blob.length - 8)); // 8-byte tag, no authTagLength -> TypeError

// after
const tagLen = 8;
const d = crypto.createDecipheriv('aes-128-gcm', key, iv, { authTagLength: tagLen });
d.setAuthTag(blob.subarray(blob.length - tagLen));
Defensive patterns

Strategy: validation

Validate before calling

function gcmDecipher(key: Buffer, iv: Buffer, tag: Buffer): crypto.DecipherGCM {
  if (tag.length !== 16)
    return crypto.createDecipheriv('aes-128-gcm', key, iv, { authTagLength: tag.length });
  return crypto.createDecipheriv('aes-128-gcm', key, iv);
}
// gcmDecipher(key, iv, tag).setAuthTag(tag) never throws the length TypeError

Try / catch

try { d.setAuthTag(tag); } catch (e) { if (e.message.startsWith('Invalid authentication tag length')) { d = crypto.createDecipheriv(algo, key, iv, { authTagLength: tag.length }); d.setAuthTag(tag); } else throw e; }

Prevention

When it happens

Trigger: createDecipheriv('aes-128-gcm', key, iv) followed by setAuthTag(tag.subarray(0, 8)) — a truncated tag without a declared length; interop with peers (Java/GnuTLS/BoringSSL) configured for short GCM tags; accidentally passing ciphertext, IV, or a mis-sliced portion of the payload instead of the tag.

Common situations: Upgrading Node/Deno where the DEP0182 warning became a hard error; splitting payload||tag blobs with an off-by-one slice length; protocols negotiated for 96-bit tags while the decrypt code assumes defaults.

Understand the failure class

Related errors


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