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
- Encrypt as a stream: feed update() with fixed-size chunks (e.g. 64 KiB to 1 MiB) as data is read.
- Do not load the whole payload into memory; pipe a read stream through the cipher.
- 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
- Design encryption as a streaming pipeline from day one; never buffer whole files.
- Chunk large inputs explicitly (64 KiB-1 MiB per update is a safe habit).
- Check byteLength of untrusted-sized inputs before one-shot update calls.
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
- ERR_INVALID_ARG_TYPE
- ERR_CRYPTO_UNKNOWN_CIPHER
- ERR_CRYPTO_INVALID_STATE
- ERR_UNKNOWN_ENCODING
- ERR_INVALID_ARG_VALUE
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/dc43ccc7a7be94fd.
Report an issue: GitHub.