denoland/deno · error · DOMException
HKDF keys are not extractable
Error message
HKDF keys are not extractable
What it means
Thrown by SecretKeyObject.toCryptoKey() when a secret KeyObject is converted to a WebCrypto CryptoKey with algorithm name 'HKDF' and extractable=true. WebCrypto requires HKDF key material to be non-extractable, since the raw IKM (input keying material) must stay hidden inside the CryptoKey. Deno enforces the rule with a DOMException of type SyntaxError, consistent with Node.js and browsers.
Source
Thrown at ext/node/polyfills/internal/crypto/keys.ts:779
"SyntaxError",
);
}
if (
usages.length > 0 &&
ArrayPrototypeSome(
usages,
(u: string) =>
!ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
)
) {
throw new DOMException(
"Unsupported key usage for a PBKDF2 key",
"SyntaxError",
);
}
} else if (algName === "HKDF") {
if (extractable) {
throw new DOMException(
"HKDF keys are not extractable",
"SyntaxError",
);
}
if (
usages.length > 0 &&
ArrayPrototypeSome(
usages,
(u: string) =>
!ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
)
) {
throw new DOMException(
"Unsupported key usage for an HKDF key",
"SyntaxError",
);
}
} else if (algName === "HMAC") {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass extractable=false when the algorithm is HKDF
- Keep the IKM bytes in your own buffer if you need to re-derive later, or export them via keyObject.export() before conversion
- Force extractable=false for all derivation algorithms in shared wrappers
Example fix
// before
const key = createSecretKey(ikm).toCryptoKey('HKDF', true, ['deriveKey']); // throws
// after
const key = createSecretKey(ikm).toCryptoKey('HKDF', false, ['deriveKey']); Defensive patterns
Strategy: validation
Validate before calling
const isDerivationAlg = (name) => name === 'PBKDF2' || name === 'HKDF'; const algName = typeof algorithm === 'string' ? algorithm : algorithm.name; const extractable = isDerivationAlg(algName) ? false : requestedExtractable; const key = secretKeyObject.toCryptoKey(algorithm, extractable, usages);
Type guard
function isNonExtractableOnlyAlgorithm(name: string): boolean {
return name === 'PBKDF2' || name === 'HKDF';
} Try / catch
try {
const key = secretKeyObject.toCryptoKey('HKDF', extractable, usages);
} catch (e) {
if (e instanceof DOMException && e.name === 'SyntaxError' && e.message.includes('not extractable')) {
// log caller config and fall back to extractable=false
} else throw e;
} Prevention
- Treat HKDF IKM like PBKDF2 password material: never request extraction
- Store the IKM in your own secret store if re-derivation is needed
- Centralize the derivation-algorithm rules so PBKDF2 and HKDF cannot drift apart
When it happens
Trigger: Calling createSecretKey(ikm).toCryptoKey('HKDF', true, ['deriveBits']) — extractable=true with algorithm name 'HKDF'.
Common situations: Shared 'import any secret' helpers that thread one extractable flag to every algorithm; copying PBKDF2-adjacent sample code; HKDF salt/IKM handling refactored from raw-buffer code that assumed the bytes stay readable.
Related errors
- PBKDF2 keys are not extractable
- Unsupported key usage for an HKDF key
- operation not supported for this keytype
- ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY
- Failed to get ECDH private key
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/56041fb48f951697.
Report an issue: GitHub.