denoland/deno · error · ERR_CRYPTO_HASH_FINALIZED
ERR_CRYPTO_HASH_FINALIZED
ERR_CRYPTO_HASH_FINALIZED
Error message
Digest already called
What it means
Hash.update() funnels its native op results through unwrapErr(): when op_node_hash_update / op_node_hash_update_str returns false, the hash handle can no longer accept data and ERR_CRYPTO_HASH_FINALIZED ('Digest already called') is thrown. A hash is single-shot — once digest() consumes it, update() on the same object is illegal.
Source
Thrown at ext/node/polyfills/internal/crypto/hash.ts:73
isArrayBufferView,
} = core.loadExtScript("ext:deno_node/internal/util/types.ts");
const {
FunctionPrototypeCall,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
ReflectApply,
SafeArrayIterator,
StringFromCharCode,
StringPrototypeToLowerCase,
Symbol,
TypedArrayPrototypeGetByteLength,
Uint8Array,
Uint8ArrayPrototype,
} = primordials;
function unwrapErr(ok: boolean) {
if (!ok) throw new ERR_CRYPTO_HASH_FINALIZED();
}
const kHandle = Symbol("kHandle");
const kFinalized = Symbol("kFinalized");
let warnedShakeOutputLength = false;
function Hash(
algorithm: string | Hasher,
options?: { outputLength?: number },
): Hash {
if (!ObjectPrototypeIsPrototypeOf(Hash.prototype, this)) {
return new Hash(algorithm, options);
}
const isCopy = ObjectPrototypeIsPrototypeOf(Hasher.prototype, algorithm);
if (!isCopy) {
validateString(algorithm, "algorithm");
}View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Create a fresh crypto.createHash(algorithm) for every digest you need — Hash objects are cheap and not reusable
- If you need the running state later, call h.copy() BEFORE digest() and digest the copy: const h2 = h.copy(); h2.digest('hex')
- Remove intermediate digest() calls used for logging, or route them through a copy
Example fix
// before
const h = crypto.createHash('sha256');
h.update(chunk1);
console.log(h.digest('hex')); // finalized
h.update(chunk2); // throws
// after
const h = crypto.createHash('sha256');
h.update(chunk1);
const h2 = h.copy();
console.log(h2.digest('hex')); // digest the copy
h.update(chunk2); Defensive patterns
Strategy: validation
Validate before calling
// Own the lifecycle: one Hash per digest, copy before consuming.
class SafeHash {
#h = crypto.createHash('sha256');
#done = false;
update(data: string | Buffer) {
if (this.#done) throw new Error('SafeHash already digested');
this.#h.update(data);
return this;
}
snapshot() { return this.#done ? null : this.#h.copy(); }
digest(enc?: string) { this.#done = true; return this.#h.digest(enc); }
} Try / catch
catch (e) { if ((e as NodeJS.ErrnoException).code === 'ERR_CRYPTO_HASH_FINALIZED') { /* start a fresh createHash and re-feed the buffered input */ } throw e; } Prevention
- Create a new Hash per digest instead of reusing instances
- copy() before digest() when the running state must survive
- Remove debug/logging code that digests early
When it happens
Trigger: const h = crypto.createHash('sha256'); h.digest('hex'); h.update('more'); — any update after digest. Common in streams when a final chunk arrives after the hash object was finalized, or when a helper digests early for logging and the caller keeps updating.
Common situations: Reusing one Hash instance across requests or loop iterations; middleware that computes an intermediate digest for debug logging; piping a stream where 'end' handling digests the hash before a late write.
Related errors
- ERR_INVALID_ARG_TYPE
- ERR_OSSL_EVP_NOT_XOF_OR_INVALID_LENGTH
- ERR_INVALID_ARG_TYPE
- ERR_CRYPTO_INVALID_DIGEST
- Invalid digest: ${algorithm}
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/8bac5bdaa5d48083.
Report an issue: GitHub.