Automattic/mongoose · error · Error

Cannot have duplicate keys in discriminators with encryption

Error message

Cannot have duplicate keys in discriminators with encryption. key=${pathname}

What it means

For encrypted collections using discriminators, Mongoose accumulates each schema in a collection namespace into one driver-level encryption mapping (csfleMappings/qeMappings). A non-root discriminator schema may not introduce a path that the namespace mapping already contains as an encrypted field - the driver's schemaMap/encryptedFieldsMap is per collection, so the same key cannot be declared encrypted twice. The Error names the offending path.

Source

Thrown at lib/drivers/node-mongodb-native/connection.js:386

  // If discriminators are configured for the collection, there might be multiple models
  // pointing to the same namespace.  For this scenario, we merge all the schemas for each namespace
  // into a single schema and then generate a schemaMap/encryptedFieldsMap for the combined schema.
  for (const model of encryptedModels) {
    const { schema, collection: { collectionName } } = model;
    const namespace = `${this.$dbName}.${collectionName}`;
    const mappings = schema.encryptionType() === 'csfle' ? csfleMappings : qeMappings;

    mappings[namespace] ??= new Schema({}, { encryptionType: schema.encryptionType() });

    const isNonRootDiscriminator = schema.discriminatorMapping && !schema.discriminatorMapping.isRoot;
    if (isNonRootDiscriminator) {
      const rootSchema = schema._baseSchema;
      schema.eachPath((pathname) => {
        if (rootSchema.path(pathname)) return;
        if (!mappings[namespace]._hasEncryptedField(pathname)) return;

        throw new Error(`Cannot have duplicate keys in discriminators with encryption. key=${pathname}`);
      });
    }

    mappings[namespace].add(schema);
  }

  const schemaMap = Object.fromEntries(Object.entries(csfleMappings).map(
    ([namespace, schema]) => ([namespace, schema._buildSchemaMap()])
  ));

  const encryptedFieldsMap = Object.fromEntries(Object.entries(qeMappings).map(
    ([namespace, schema]) => ([namespace, schema._buildEncryptedFields()])
  ));

  return {
    schemaMap, encryptedFieldsMap
  };
};

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Declare shared encrypted paths once in the root/base schema; discriminators inherit them.
  2. Rename the colliding path in one discriminator if the fields are genuinely different.
  3. Drop encryption from the duplicated path in the discriminator if the base already covers it.

Example fix

// before
const Base = new Schema({ kind: String });
Base.discriminator('A', new Schema({ card: { type: String, encrypted: true } }));
Base.discriminator('B', new Schema({ card: { type: String, encrypted: true } })); // duplicate encrypted key

// after: shared encrypted path lives in the root schema
const Base = new Schema({ kind: String, card: { type: String, encrypted: true } });
Base.discriminator('A', new Schema({ aField: String }));
Base.discriminator('B', new Schema({ bField: String }));
Defensive patterns

Strategy: validation

Validate before calling

// detect duplicate paths across sibling discriminators before wiring
function assertNoDuplicateDiscriminatorPaths(baseSchema, discSchemas) {
  const seen = new Map();
  for (const [name, s] of Object.entries(discSchemas)) {
    for (const path of Object.keys(s.paths)) {
      if (baseSchema.paths[path]) continue;
      if (seen.has(path)) {
        throw new Error(`duplicate discriminator path '${path}' in ${name} and ${seen.get(path)}`);
      }
      seen.set(path, name);
    }
  }
}

Prevention

When it happens

Trigger: Two sibling discriminator schemas under one base model (same collection) that both define the same path as an encrypted field, e.g. discriminators A and B each with an encrypted card path; the second discriminator trips the check mappings[namespace]._hasEncryptedField(pathname) and throws with key=<path>.

Common situations: Refactoring single-table inheritance where each subtype previously owned its collection; copy-pasting encrypted field definitions between discriminator schemas; migrating existing encrypted fields into a discriminator hierarchy.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/c247b13348dc0fdc. Report an issue: GitHub.