{"record":{"id":"c247b13348dc0fdc","repo":"Automattic/mongoose","slug":"cannot-have-duplicate-keys-in-discriminators-with","errorCode":null,"errorMessage":"Cannot have duplicate keys in discriminators with encryption. key=${pathname}","messagePattern":"Cannot have duplicate keys in discriminators with encryption\\. key=(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/drivers/node-mongodb-native/connection.js","lineNumber":386,"sourceCode":"\n  // If discriminators are configured for the collection, there might be multiple models\n  // pointing to the same namespace.  For this scenario, we merge all the schemas for each namespace\n  // into a single schema and then generate a schemaMap/encryptedFieldsMap for the combined schema.\n  for (const model of encryptedModels) {\n    const { schema, collection: { collectionName } } = model;\n    const namespace = `${this.$dbName}.${collectionName}`;\n    const mappings = schema.encryptionType() === 'csfle' ? csfleMappings : qeMappings;\n\n    mappings[namespace] ??= new Schema({}, { encryptionType: schema.encryptionType() });\n\n    const isNonRootDiscriminator = schema.discriminatorMapping && !schema.discriminatorMapping.isRoot;\n    if (isNonRootDiscriminator) {\n      const rootSchema = schema._baseSchema;\n      schema.eachPath((pathname) => {\n        if (rootSchema.path(pathname)) return;\n        if (!mappings[namespace]._hasEncryptedField(pathname)) return;\n\n        throw new Error(`Cannot have duplicate keys in discriminators with encryption. key=${pathname}`);\n      });\n    }\n\n    mappings[namespace].add(schema);\n  }\n\n  const schemaMap = Object.fromEntries(Object.entries(csfleMappings).map(\n    ([namespace, schema]) => ([namespace, schema._buildSchemaMap()])\n  ));\n\n  const encryptedFieldsMap = Object.fromEntries(Object.entries(qeMappings).map(\n    ([namespace, schema]) => ([namespace, schema._buildEncryptedFields()])\n  ));\n\n  return {\n    schemaMap, encryptedFieldsMap\n  };\n};","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/drivers/node-mongodb-native/connection.js#L368-L404","documentation":"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.","triggerScenarios":"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>.","commonSituations":"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.","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."],"exampleFix":"// before\nconst Base = new Schema({ kind: String });\nBase.discriminator('A', new Schema({ card: { type: String, encrypted: true } }));\nBase.discriminator('B', new Schema({ card: { type: String, encrypted: true } })); // duplicate encrypted key\n\n// after: shared encrypted path lives in the root schema\nconst Base = new Schema({ kind: String, card: { type: String, encrypted: true } });\nBase.discriminator('A', new Schema({ aField: String }));\nBase.discriminator('B', new Schema({ bField: String }));","handlingStrategy":"validation","validationCode":"// detect duplicate paths across sibling discriminators before wiring\nfunction assertNoDuplicateDiscriminatorPaths(baseSchema, discSchemas) {\n  const seen = new Map();\n  for (const [name, s] of Object.entries(discSchemas)) {\n    for (const path of Object.keys(s.paths)) {\n      if (baseSchema.paths[path]) continue;\n      if (seen.has(path)) {\n        throw new Error(`duplicate discriminator path '${path}' in ${name} and ${seen.get(path)}`);\n      }\n      seen.set(path, name);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["mongoose","discriminators","encryption","csfle","queryable-encryption","schema-design"],"backgroundTag":"discriminator-schema-conflict","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}