mongodb/node-mongodb-native · error · MongoCryptInvalidArgumentError

"options" cannot contain both "keyId" and "keyAltName"

Error message

"options" cannot contain both "keyId" and "keyAltName"

What it means

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.

Solutions

  1. Provide exactly one of options.keyId (the UUID Binary of the data encryption key) or options.keyAltName (the alternate name string) — never both.
  2. Audit the options object right before the encrypt call and delete the unused field: if you resolved a keyId, delete options.keyAltName.
  3. If you need deterministic key selection, store/retrieve a single key by keyAltName via getKeyByAltName and pass only its _id as keyId.

Example fix

// before
await clientEncryption.encrypt(value, {
  algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-RANDOM',
  keyId: dataKey._id,
  keyAltName: 'customerKey' // throws: both set
});
// after
await clientEncryption.encrypt(value, {
  algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-RANDOM',
  keyId: dataKey._id
});
Defensive patterns

Strategy: validation

Validate before calling

function assertEncryptKeyOptions(options) {
  const hasId = options.keyId != null;
  const hasAlt = options.keyAltName != null;
  if (hasId && hasAlt) {
    throw new Error('Provide exactly one of options.keyId or options.keyAltName, not both.');
  }
  if (!hasId && !hasAlt) {
    throw new Error('One of options.keyId or options.keyAltName is required.');
  }
}
// call before clientEncryption.encrypt(...)
assertEncryptKeyOptions(options);

Type guard

function isKeyAltNameString(v: unknown): v is string {
  return v == null || typeof v === 'string';
}

Try / catch

try {
  await clientEncryption.encrypt(value, options);
} catch (e) {
  if (e instanceof MongoCryptInvalidArgumentError && /both "keyId" and "keyAltName"/.test(e.message)) {
    // fix the options and retry with one key identifier
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/29d9d3152b2c2bcf. Report an issue: GitHub.

Appendix: source

Thrown at src/client-side-encryption/client_encryption.ts:780

      algorithm,
      keyId,
      keyAltName,
      contentionFactor,
      queryType,
      rangeOptions,
      stringOptions,
      textOptions
    } = options;
    const contextOptions: ExplicitEncryptionContextOptions = {
      expressionMode,
      algorithm
    };
    if (keyId) {
      contextOptions.keyId = keyId.buffer;
    }
    if (keyAltName) {
      if (keyId) {
        throw new MongoCryptInvalidArgumentError(
          `"options" cannot contain both "keyId" and "keyAltName"`
        );
      }
      if (typeof keyAltName !== 'string') {
        throw new MongoCryptInvalidArgumentError(
          `"options.keyAltName" must be of type string, but was of type ${typeof keyAltName}`
        );
      }

      contextOptions.keyAltName = serialize({ keyAltName });
    }
    if (typeof contentionFactor === 'number' || typeof contentionFactor === 'bigint') {
      contextOptions.contentionFactor = contentionFactor;
    }
    if (typeof queryType === 'string') {
      contextOptions.queryType = queryType;
    }

View on GitHub (pinned to dce7939f86)