{"record":{"id":"57a0ffc6ecd75e65","repo":"denoland/deno","slug":"usages-cannot-be-empty-when-importing-a-private-ke","errorCode":null,"errorMessage":"Usages cannot be empty when importing a private key.","messagePattern":"Usages cannot be empty when importing a private key\\.","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/crypto/keys.ts","lineNumber":910,"sourceCode":"    };\n  }\n\n  toCryptoKey(\n    algorithm: string | object,\n    extractable: boolean,\n    usages: string[],\n  ): CryptoKey {\n    const algName = typeof algorithm === \"string\"\n      ? algorithm\n      : (algorithm as { name: string }).name;\n\n    _validateAsymmetricKeyAlgorithm(this, algName);\n    if (typeof algorithm === \"object\") {\n      _validateEcNamedCurve(this, algorithm);\n    }\n\n    if (usages.length === 0) {\n      throw new DOMException(\n        \"Usages cannot be empty when importing a private key.\",\n        \"SyntaxError\",\n      );\n    }\n\n    const pkcs8Data = Buffer.from(\n      op_node_export_private_key_der(this[kHandle], \"pkcs8\", null, null),\n    );\n    return importCryptoKeySync(\n      \"pkcs8\",\n      pkcs8Data,\n      algorithm,\n      extractable,\n      usages,\n    );\n  }\n\n  export(options: any) {","sourceCodeStart":892,"sourceCodeEnd":928,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/crypto/keys.ts#L892-L928","documentation":"Thrown by PrivateKeyObject.toCryptoKey() when an asymmetric private KeyObject is converted to a WebCrypto CryptoKey with an empty usages array. A private key always corresponds to at least one operation (sign, decrypt, deriveKey, deriveBits), so WebCrypto import rules reject empty usages with a SyntaxError DOMException. The check runs after the algorithm/curve validation, so it only fires once the algorithm name matches the key type.","triggerScenarios":"generateKeyPairSync('ec', { namedCurve: 'prime256v1' }).privateKey.toCryptoKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, []).","commonSituations":"Wrappers with an optional usages parameter that defaults to empty; converting Node KeyObjects to CryptoKeys in a generic pipeline where usages were never threaded through; porting code that generated CryptoKeys directly and later switched to KeyObject-first APIs.","solutions":["Pass the operations the key performs: ['sign'] for ECDSA/RSA-PSS, ['decrypt'] for RSA-OAEP, ['deriveKey','deriveBits'] for ECDH/X25519","Keep one algorithm-to-default-usages mapping and use it everywhere","Fail fast in your wrapper if usages is empty, with a message naming the algorithm"],"exampleFix":"// before\nconst ck = privateKey.toCryptoKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, []); // throws\n\n// after\nconst ck = privateKey.toCryptoKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign']);","handlingStrategy":"validation","validationCode":"const DEFAULT_USAGES: Record<string, string[]> = {\n  ECDSA: ['sign'], 'RSA-PSS': ['sign'], 'RSA-OAEP': ['decrypt'],\n  ECDH: ['deriveBits'], X25519: ['deriveBits'],\n};\nconst usages = requested.length > 0 ? requested : DEFAULT_USAGES[algName];\nif (!usages?.length) throw new Error(`no usages for ${algName}`);\nconst ck = privateKey.toCryptoKey(algorithm, extractable, usages);","typeGuard":null,"tryCatchPattern":"try {\n  ck = privateKey.toCryptoKey(algorithm, extractable, usages);\n} catch (e) {\n  if (e instanceof DOMException && e.name === 'SyntaxError' && e.message.includes('Usages cannot be empty')) {\n    ck = privateKey.toCryptoKey(algorithm, extractable, ['sign']);\n  } else throw e;\n}","preventionTips":["Never give wrappers an optional usages parameter that can default to empty","Map each private key algorithm to its intended operations in one place","Test the private-key conversion path for every algorithm you support"],"tags":["crypto","webcrypto","private-key","key-usage","node-compat"],"backgroundTag":"webcrypto-empty-key-usages","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}