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
- Declare shared encrypted paths once in the root/base schema; discriminators inherit them.
- Rename the colliding path in one discriminator if the fields are genuinely different.
- 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
- Put every encrypted field shared by subtypes in the root schema once.
- Adopt a review rule: discriminator schemas in encrypted collections only add subtype-specific plaintext paths.
- Compile base plus all discriminators in a startup smoke test so duplicates fail at boot, not on first query.
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
- Must provide `autoEncryption` when connecting with encrypted
- Discriminator "${doc[discriminatorKey]}" not found for model
- Arguments must be aggregate pipeline operators
- Invalid addFields() argument. Must be an object
- Invalid project() argument. Must be string or object
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/c247b13348dc0fdc.
Report an issue: GitHub.