Automattic/mongoose · error · MongooseError

The provided class ${name} must extend Model

Error message

The provided class ${name} must extend Model

What it means

Model.discriminator() accepts either (name, schema) or a pre-defined class as its first argument. When a function/class is passed, it must extend Model — Mongoose wires discriminator inheritance through the Model prototype chain and cannot attach arbitrary constructors. This MongooseError is thrown when the provided class's prototype is not an instance of Model (including classes from a different mongoose copy).

Source

Thrown at lib/model.js:1005

 * @param {string} name discriminator model name
 * @param {Schema} schema discriminator model schema
 * @param {object|string} [options] If string, same as `options.value`.
 * @param {string} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
 * @param {boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
 * @param {boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
 * @param {boolean} [options.mergeHooks=true] By default, Mongoose merges the base schema's hooks with the discriminator schema's hooks. Set this option to `false` to make Mongoose use the discriminator schema's hooks instead.
 * @param {boolean} [options.mergePlugins=true] By default, Mongoose merges the base schema's plugins with the discriminator schema's plugins. Set this option to `false` to make Mongoose use the discriminator schema's plugins instead.
 * @return {Model} The newly created discriminator model
 * @api public
 */

Model.discriminator = function(name, schema, options) {
  let model;
  if (typeof name === 'function') {
    model = name;
    name = utils.getFunctionName(model);
    if (!(model.prototype instanceof Model)) {
      throw new MongooseError('The provided class ' + name + ' must extend Model');
    }
  }

  options = options || {};
  const value = utils.isPOJO(options) ? options.value : options;
  const clone = typeof options.clone === 'boolean' ? options.clone : true;
  const mergePlugins = typeof options.mergePlugins === 'boolean' ? options.mergePlugins : true;
  const overwriteModels = typeof options.overwriteModels === 'boolean' ? options.overwriteModels : false;

  _checkContext(this, 'discriminator');

  if (utils.isObject(schema) && !schema.instanceOfSchema) {
    schema = new Schema(schema);
  }
  if (schema instanceof Schema && clone) {
    schema = schema.clone();
  }

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Let Mongoose create the class: const Clicked = Events.discriminator('Clicked', clickedSchema)
  2. Only pass a class when it genuinely extends Model from the same mongoose import: class Clicked extends Model {}
  3. Do not pass plain constructors or domain classes expecting them to become models

Example fix

// before
class ClickedEvent { constructor(props) { Object.assign(this, props); } }
Events.discriminator(ClickedEvent, clickedSchema); // throws: must extend Model

// after
const ClickedEvent = Events.discriminator('Clicked', clickedSchema);
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate a class before passing it to discriminator()
function isModelClass(cls) {
  return typeof cls === 'function' && (cls.prototype instanceof mongoose.Model || cls === mongoose.Model);
}
if (isModelClass(ClickedEvent)) Events.discriminator(ClickedEvent, clickedSchema);

Type guard

function isModelClass(cls) {
  return typeof cls === 'function' && (cls.prototype instanceof mongoose.Model || cls === mongoose.Model);
}

Try / catch

try {
  Events.discriminator(nameOrClass, schema);
} catch (err) {
  if (err instanceof mongoose.MongooseError && /must extend Model/.test(err.message)) {
    // re-register with just a name: Events.discriminator('Clicked', schema)
  } else throw err;
}

Prevention

When it happens

Trigger: BaseModel.discriminator(SomeRandomClass, schema), BaseModel.discriminator(class Event {}, eventSchema), or passing a class that extends a Model imported from a duplicate mongoose instance; then calling the discriminator registration.

Common situations: Trying to hook discriminators into existing domain classes; TypeScript classes declared without `extends Model`; plugins registering user-supplied classes; duplicate-mongoose setups where the extends chain crosses copies.

Related errors


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