denoland/deno · error · DOMException
PBKDF2 keys are not extractable
Error message
PBKDF2 keys are not extractable
What it means
Thrown by SecretKeyObject.toCryptoKey() in Deno's node:crypto polyfill when a secret KeyObject is converted to a WebCrypto CryptoKey with algorithm name 'PBKDF2' and extractable=true. The WebCrypto specification requires PBKDF2 password material to be non-extractable, because the raw password must never be read back out of the CryptoKey. Deno enforces this with a DOMException of type SyntaxError, matching Node.js and browsers. It is a usage error, not a problem with the key itself.
Source
Thrown at ext/node/polyfills/internal/crypto/keys.ts:759
extractable: boolean,
usages: string[],
): CryptoKey {
const algName = typeof algorithm === "string"
? algorithm
: (algorithm as { name: string }).name;
const rawData = new Uint8Array(op_node_export_secret_key(this[kHandle]));
if (TypedArrayPrototypeGetByteLength(rawData) === 0) {
throw new DOMException(
"Zero-length key is not supported",
"DataError",
);
}
if (algName === "PBKDF2") {
if (extractable) {
throw new DOMException(
"PBKDF2 keys are not extractable",
"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") {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass extractable=false when the algorithm is PBKDF2 (or HKDF)
- If you need the raw password bytes, keep them in your own buffer or call keyObject.export() on the KeyObject instead of extracting a CryptoKey
- In generic wrappers, force extractable=false for derivation algorithms before calling toCryptoKey/importKey
Example fix
// before
const key = createSecretKey(pw).toCryptoKey('PBKDF2', true, ['deriveBits']); // DOMException SyntaxError
// after
const key = createSecretKey(pw).toCryptoKey('PBKDF2', false, ['deriveBits']); 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(algorithm, extractable, usages);
} catch (e) {
if (e instanceof DOMException && e.name === 'SyntaxError' && e.message.includes('not extractable')) {
// retry once with extractable=false, or surface a config error
} else throw e;
} Prevention
- Default extractable to false; only opt in for algorithms that actually support extraction
- Keep one algorithm->(extractable, allowedUsages) rules table instead of hand-passing flags
- Remember PBKDF2/HKDF raw material must live in your own buffer if you need it again
When it happens
Trigger: Calling createSecretKey(password).toCryptoKey('PBKDF2', true, ['deriveBits']) — or any KeyObject-to-CryptoKey interop path that forwards extractable=true while the algorithm name is 'PBKDF2'.
Common situations: Copy-pasting a subtle.importKey('raw', ..., true, ...) example written for HMAC/AES into PBKDF2 code; generic key wrappers that always request extractable keys; porting browser snippets where the extractable flag comes from a variable the user controls.
Related errors
- Unsupported key usage for a PBKDF2 key
- HKDF keys are not extractable
- 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/a1fdf489142ba6b7.
Report an issue: GitHub.