denoland/deno · error · Error

Trying to add data in unsupported state

Error message

Trying to add data in unsupported state

What it means

update() rejects inputs whose byte length is >= 2^31 - 1 (2147483647, about 2 GiB), matching the INT_MAX single-call limit of Node/OpenSSL's CipherBase. This is a plain Error (no code property) reusing Node's literal message 'Trying to add data in unsupported state'.

Source

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

  outputEncoding: any = getDefaultEncoding(),
): Buffer | string {
  if (this._finalized) {
    throw new ERR_CRYPTO_INVALID_STATE("update");
  }

  validateCipherUpdateData(data);

  let buf = data;
  if (typeof data === "string") {
    buf = Buffer.from(data, inputEncoding);
  } else {
    buf = toFastBufferView(data);
  }
  const inputByteLength = getArrayBufferViewByteLength(buf);

  // Match Node.js/OpenSSL behavior: reject inputs >= INT_MAX bytes
  if (inputByteLength >= 2 ** 31 - 1) {
    throw new Error("Trying to add data in unsupported state");
  }

  _lazyInitCipherDecoder(this, outputEncoding);

  if (this._isAesWrap) {
    const output = Buffer.from(
      op_node_aes_wrap_key(
        this._aesWrapAlgorithm,
        this._aesWrapKey,
        this._aesWrapIv,
        buf,
      ),
    );
    if (outputEncoding !== "buffer") {
      return this._decoder!.write(output);
    }
    return output;
  }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Encrypt as a stream: feed update() with fixed-size chunks (e.g. 64 KiB to 1 MiB) as data is read.
  2. Do not load the whole payload into memory; pipe a read stream through the cipher.
  3. If pre-validation is needed, check the input's byte length < 2 ** 31 - 1 before calling update().

Example fix

// before
const whole = readEntireFile('backup.tar'); // > 2 GiB
cipher.update(whole); // Error: Trying to add data in unsupported state

// after
const CHUNK = 1 << 20; // 1 MiB
for await (const chunk of readFileIter('backup.tar')) {
  const piece = chunk.length > CHUNK ? chunk : chunk; // stream naturally chunked
  out.write(cipher.update(piece.subarray(0, CHUNK)));
  if (chunk.length > CHUNK) out.write(cipher.update(chunk.subarray(CHUNK)));
}
out.write(cipher.final());
Defensive patterns

Strategy: validation

Validate before calling

const MAX_UPDATE_BYTES = 2 ** 31 - 2; // stay under the 2^31-1 limit
function assertUpdatableSize(input: Buffer | ArrayBufferView | string): void {
  const len = typeof input === 'string' ? Buffer.byteLength(input) : input.byteLength;
  if (len >= 2 ** 31 - 1)
    throw new RangeError(`input of ${len} bytes exceeds single-update limit; chunk it`);
}
function* chunks(buf: Buffer, size = 1 << 20): Generator<Buffer> {
  for (let i = 0; i < buf.length; i += size) yield buf.subarray(i, i + size);
}

Try / catch

try { out = cipher.update(buf); } catch (e) { if (e.message === 'Trying to add data in unsupported state' && buf.length >= 2 ** 31 - 1) { out = Buffer.concat([...chunks(buf)].map((c) => cipher.update(c))); } else throw e; }

Prevention

When it happens

Trigger: cipher.update(hugeBuffer) where the buffer was built by concatenating an entire file; passing a giant preallocated TypedArray; Batch jobs that accumulate logs/media into one buffer before a single encrypt call.

Common situations: Whole-file encryption of large media, dumps, or backups; log-shipping pipelines that batch everything into memory; switching from per-chunk encryption to one-shot encryption without chunking.

Related errors


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