Automattic/mongoose · error · OverwriteModelError

Cannot overwrite `${name}` model once compiled.

Error message

Cannot overwrite `${name}` model once compiled.

What it means

Thrown by Model.discriminator() (via OverwriteModelError) when a discriminator name is already registered on the same connection (this.db.models[name] exists). Mongoose refuses to silently replace a compiled model because existing documents, queries, and hooks hold references to the old constructor. The guard is bypassed only when the schema option `overwriteModels` is true or the `overwriteModels` argument is passed as true.

Source

Thrown at lib/model.js:1026

  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();
  }

  schema = discriminator(this, name, schema, value, mergePlugins, options.mergeHooks, overwriteModels);
  if (this.db.models[name] && !schema.options.overwriteModels && !overwriteModels) {
    throw new OverwriteModelError(name);
  }

  schema.$isRootDiscriminator = true;
  schema.$globalPluginsApplied = true;

  model = this.db.model(model || name, schema, this.$__collection.name);
  this.discriminators[name] = model;
  const d = this.discriminators[name];
  Object.setPrototypeOf(d.prototype, this.prototype);
  Object.defineProperty(d, 'baseModelName', {
    value: this.modelName,
    configurable: true,
    writable: false
  });

  // apply methods and statics
  applyMethods(d, schema);
  applyStatics(d, schema);

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Reuse the existing model instead of re-registering: `const Clicked = Event.discriminators['ClickedLink'] ?? Event.discriminator('ClickedLink', clickedSchema)`
  2. Delete the stale registration first: `mongoose.connection.deleteModel('ClickedLink')` before recompiling
  3. Pass the overwrite flag: `Event.discriminator('ClickedLink', clickedSchema, { value: ..., mergeHooks: ... }, true)` or set `overwriteModels: true` in the schema options
  4. Centralize all model definitions in one module that runs once per process, and export the compiled models from there

Example fix

// before (runs on every hot reload)
Event.discriminator('ClickedLink', clickedSchema); // OverwriteModelError on 2nd run

// after
const ClickedLink =
  Event.discriminators['ClickedLink'] ||
  Event.discriminator('ClickedLink', clickedSchema);
Defensive patterns

Strategy: validation

Validate before calling

// Before registering, check every registry the discriminator would land in
const getDiscriminator = (Model, name, schema) =>
  Model.discriminators?.[name] ||
  mongoose.models[name] ||
  mongoose.connection.models[name] ||
  Model.discriminator(name, schema);

Prevention

When it happens

Trigger: Calling `Event.discriminator('ClickedLink', clickedSchema)` twice with the same name on one connection; a discriminator name that collides with an already-registered base or sibling model; re-executing a model-definition module against a live connection (Next.js/NestJS HMR, Jest watch) so `mongoose.model()` runs again.

Common situations: Hot-reloading dev servers that re-import model files; test suites that recompile models on every run without resetting mongoose; monorepos where the same model module is loaded twice through different resolve paths; serverless environments that re-use a cached connection across warm invocations.

Related errors


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