denoland/deno · critical
Failed to allocate EVP_CIPHER_CTX
Error message
Failed to allocate EVP_CIPHER_CTX
What it means
When node:crypto creates a ChaCha20-Poly1305 cipher via createCipheriv, Deno allocates an OpenSSL EVP_CIPHER_CTX. If that allocation fails (malloc returns null), init reports ContextAllocation and Deno panics — effectively an out-of-memory abort, not a crypto-usage error. Bad key/IV/tag inputs return normal CipherErrors; only the context allocation failure panics.
Source
Thrown at ext/node_crypto/cipher.rs:978
}
ChaCha20(Box::new(ChaCha20Cipher::new(key, iv)))
}
"chacha20-poly1305" => {
if key.len() != 32 {
return Err(CipherError::InvalidKeyLength);
}
if iv.len() != 12 {
return Err(CipherError::InvalidInitializationVector);
}
let tag_len = auth_tag_length.unwrap_or(16);
if !is_valid_chacha20_poly1305_tag_length(tag_len) {
return Err(CipherError::InvalidAuthTag(tag_len));
}
ChaCha20Poly1305(Box::new(
ChaCha20Poly1305Cipher::new(key, iv, tag_len, true).map_err(|e| {
match e {
CipherInitError::ContextAllocation => {
panic!("Failed to allocate EVP_CIPHER_CTX")
}
CipherInitError::InitFailed => CipherError::InvalidKeyLength,
}
})?,
))
}
_ => return Err(CipherError::UnknownCipher(algorithm_name.to_string())),
})
}
fn set_aad(&mut self, aad: &[u8]) {
use Cipher::*;
match self {
Aes128Gcm(cipher, _) => {
cipher.set_aad(aad);
}
Aes256Gcm(cipher, _) => {
cipher.set_aad(aad);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Relieve memory pressure: raise container/cgroup limits or restart the leaking process
- Create one cipher per message (update then final), not per chunk, so contexts are released
- Check for leaked Cipher objects — a missing final() keeps contexts alive
- If memory looks healthy, update Deno — a runtime regression would affect everyone using the cipher
Example fix
// before — new cipher context per chunk
for (const chunk of chunks) {
const c = crypto.createCipheriv("chacha20-poly1305", key, iv);
out.push(c.update(chunk), c.final());
}
// after — one cipher per message; final() frees the context
const c = crypto.createCipheriv("chacha20-poly1305", key, iv);
for (const chunk of chunks) out.push(c.update(chunk));
out.push(c.final()); Defensive patterns
Strategy: fallback
Validate before calling
// pre-flight: refuse to create ciphers under memory pressure
const { rss } = Deno.memoryUsage();
const total = Deno.systemMemoryInfo()?.total ?? Infinity;
if (rss > 0.85 * total) {
throw new Error("memory pressure: defer creating new cipher contexts");
}
const c = crypto.createCipheriv("chacha20-poly1305", key, iv); Prevention
- Create one cipher per message and always call final() so contexts are freed
- Avoid createCipheriv inside tight per-chunk loops
- Raise container memory limits above the encryption workload's peak
- Watch for leaked Cipher/Decipher objects that never finalize
When it happens
Trigger: `crypto.createCipheriv("chacha20-poly1305" | "id-chacha20-poly1305-ietf", key, iv)` succeeding validation but failing at EVP_CIPHER_CTX_new because the process is out of memory.
Common situations: Containers with low memory limits doing bulk encryption; workloads creating a cipher per chunk in a hot loop without calling final(); leaks elsewhere in the process crowding out the OpenSSL allocator.
Related errors
- AAD length exceeds i32::MAX
- input length exceeds i32::MAX
- ERR_OSSL_BN_BIGNUM_TOO_LONG
- failed to open in-memory cache db
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/0c13973db3229f19.
Report an issue: GitHub.