{"record":{"id":"29d9d3152b2c2bcf","repo":"mongodb/node-mongodb-native","slug":"options-cannot-contain-both-keyid-and-keyaltn","errorCode":null,"errorMessage":"\"options\" cannot contain both \"keyId\" and \"keyAltName\"","messagePattern":"\"options\" cannot contain both \"keyId\" and \"keyAltName\"","errorType":"exception","errorClass":"MongoCryptInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/client-side-encryption/client_encryption.ts","lineNumber":780,"sourceCode":"      algorithm,\n      keyId,\n      keyAltName,\n      contentionFactor,\n      queryType,\n      rangeOptions,\n      stringOptions,\n      textOptions\n    } = options;\n    const contextOptions: ExplicitEncryptionContextOptions = {\n      expressionMode,\n      algorithm\n    };\n    if (keyId) {\n      contextOptions.keyId = keyId.buffer;\n    }\n    if (keyAltName) {\n      if (keyId) {\n        throw new MongoCryptInvalidArgumentError(\n          `\"options\" cannot contain both \"keyId\" and \"keyAltName\"`\n        );\n      }\n      if (typeof keyAltName !== 'string') {\n        throw new MongoCryptInvalidArgumentError(\n          `\"options.keyAltName\" must be of type string, but was of type ${typeof keyAltName}`\n        );\n      }\n\n      contextOptions.keyAltName = serialize({ keyAltName });\n    }\n    if (typeof contentionFactor === 'number' || typeof contentionFactor === 'bigint') {\n      contextOptions.contentionFactor = contentionFactor;\n    }\n    if (typeof queryType === 'string') {\n      contextOptions.queryType = queryType;\n    }\n","sourceCodeStart":762,"sourceCodeEnd":798,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/client-side-encryption/client_encryption.ts#L762-L798","documentation":"Thrown by Client-Side Field-Level Encryption (CSFLE) explicit encryption when both options.keyId and options.keyAltName are supplied to ClientEncryption.encrypt / encryptExpression. The driver passes key identification to libmongocrypt via exactly one of these two fields; specifying both is contradictory. It surfaces as a MongoCryptInvalidArgumentError and fails synchronously before any crypto operation runs.","triggerScenarios":"Calling clientEncryption.encrypt(value, { keyId: <Binary>, keyAltName: 'name', algorithm: ... }) with both fields set; or building an options object programmatically and accidentally merging a keyId into an object that already carries keyAltName.","commonSituations":"Copying an options object that already had keyAltName and then adding keyId (or vice versa); migrating from keyAltName-based key lookup to keyId-based lookup and forgetting to delete the old field; spreading defaults `{ keyAltName: 'default' }` into an encrypt call that also sets keyId.","solutions":["Provide exactly one of options.keyId (the UUID Binary of the data encryption key) or options.keyAltName (the alternate name string) — never both.","Audit the options object right before the encrypt call and delete the unused field: if you resolved a keyId, delete options.keyAltName.","If you need deterministic key selection, store/retrieve a single key by keyAltName via getKeyByAltName and pass only its _id as keyId."],"exampleFix":"// before\nawait clientEncryption.encrypt(value, {\n  algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-RANDOM',\n  keyId: dataKey._id,\n  keyAltName: 'customerKey' // throws: both set\n});\n// after\nawait clientEncryption.encrypt(value, {\n  algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-RANDOM',\n  keyId: dataKey._id\n});","handlingStrategy":"validation","validationCode":"function assertEncryptKeyOptions(options) {\n  const hasId = options.keyId != null;\n  const hasAlt = options.keyAltName != null;\n  if (hasId && hasAlt) {\n    throw new Error('Provide exactly one of options.keyId or options.keyAltName, not both.');\n  }\n  if (!hasId && !hasAlt) {\n    throw new Error('One of options.keyId or options.keyAltName is required.');\n  }\n}\n// call before clientEncryption.encrypt(...)\nassertEncryptKeyOptions(options);","typeGuard":"function isKeyAltNameString(v: unknown): v is string {\n  return v == null || typeof v === 'string';\n}","tryCatchPattern":"try {\n  await clientEncryption.encrypt(value, options);\n} catch (e) {\n  if (e instanceof MongoCryptInvalidArgumentError && /both \"keyId\" and \"keyAltName\"/.test(e.message)) {\n    // fix the options and retry with one key identifier\n  }\n  throw e;\n}","preventionTips":["Centralise encrypt-option construction in one helper that enforces the keyId XOR keyAltName rule.","When migrating from keyAltName to keyId, delete the old field rather than leaving it."],"tags":["csfle","client-side-encryption","validation","configuration"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}