{"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":"validation","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/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/client-side-encryption/client_encryption.ts#L762-L798","documentation":"Thrown by ClientEncryption.encrypt (MongoCryptInvalidArgumentError) when both options.keyId and options.keyAltName are provided. Encryption must be keyed by exactly one identifier; specifying both is ambiguous and the driver refuses to guess which key to use.","triggerScenarios":"Calling encrypt(value, { keyId: <binary>, keyAltName: 'myKey', algorithm: ... }); commonly happens when keyId defaults from a previous lookup and keyAltName is added for convenience.","commonSituations":"Refactoring encrypt calls to switch from keyId to keyAltName without removing the other; building options from a config that carries both fields; helper functions that always pass both.","solutions":["Pass exactly one of keyId or keyAltName.","If you have both, prefer keyId (the explicit UUID) and drop keyAltName.","Use a discriminated option builder that returns either { keyId } or { keyAltName } but never both."],"exampleFix":"// before\nawait ce.encrypt(value, { keyId, keyAltName, algorithm });\n\n// after\nawait ce.encrypt(value, { keyId, algorithm });","handlingStrategy":"validation","validationCode":"function normalizeEncryptOpts(opt) {\n  if (opt.keyId && opt.keyAltName)\n    throw new Error('Pass exactly one of keyId or keyAltName');\n  return opt;\n}","typeGuard":"type EncryptKeyOption = { keyId: Binary; keyAltName?: undefined } | { keyId?: undefined; keyAltName: string };","tryCatchPattern":"try { await ce.encrypt(value, opt); }\ncatch (err) {\n  if (err instanceof MongoCryptInvalidArgumentError && /cannot contain both/.test(err.message)) {\n    /* drop keyAltName, keep keyId */\n  } else throw err;\n}","preventionTips":["Model encrypt options as a discriminated union.","Default to keyId when you have a UUID; reserve keyAltName for named aliases only.","Code-review helper functions that assemble encrypt options."],"tags":["csfle","client-encryption","validation","encryption"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}