Automattic/mongoose · error · OverwriteModelError

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

Error message

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

What it means

Mongoose caches compiled models by name per connection. Re-calling conn.model('X', schema) when 'X' is already compiled throws OverwriteModelError ('Cannot overwrite `X` model once compiled.') unless the schema object is identical, a custom collection name is given, or overwriteModels is enabled (lib/connection.js:1472). The guard protects live model classes and existing documents from being silently swapped.

Source

Thrown at lib/connection.js:1472

  if (utils.isObject(schema)) {
    if (!schema.instanceOfSchema) {
      schema = new Schema(schema);
    } else if (!(schema instanceof this.base.Schema)) {
      schema = schema._clone(this.base.Schema);
    }
  }
  if (schema && !schema.instanceOfSchema) {
    throw new MongooseError('The 2nd parameter to `mongoose.model()` should be a ' +
      'schema or a POJO');
  }

  const defaultOptions = { cache: false, overwriteModels: this.base.options.overwriteModels };
  const opts = Object.assign(defaultOptions, options, { connection: this });
  if (this.models[name] && !collection && opts.overwriteModels !== true) {
    // model exists but we are not subclassing with custom collection
    if (schema?.instanceOfSchema && schema !== this.models[name].schema) {
      throw new MongooseError.OverwriteModelError(name);
    }
    return this.models[name];
  }

  let model;

  if (schema?.instanceOfSchema) {
    applyPlugins(schema, this.plugins, null, '$connectionPluginsApplied');

    // compile a model
    model = this.base._model(fn || name, schema, collection, opts);

    // only the first model with this name is cached to allow
    // for one-offs with custom collection names etc.
    if (!this.models[name]) {
      this.models[name] = model;
    }

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Reuse-or-define guard: const Test = mongoose.models.Test ?? mongoose.model('Test', schema)
  2. Reuse the compiled model instead: const Test = conn.model('Test') (name-only lookup)
  3. Delete before redefining: conn.deleteModel('Test')
  4. Opt in explicitly: conn.model('Test', schema, null, { overwriteModels: true }) or globally mongoose.set('overwriteModels', true)

Example fix

// before
module.exports = mongoose.model('Test', testSchema); // throws on hot reload / re-import

// after
module.exports = mongoose.models.Test ?? mongoose.model('Test', testSchema);
Defensive patterns

Strategy: validation

Validate before calling

const getModel = (name, schema, collection) =>
  conn.models[name] ?? conn.model(name, schema, collection);
// or check explicitly:
if (conn.modelNames().includes('Test')) {
  const Test = conn.model('Test'); // reuse existing
} else {
  const Test = conn.model('Test', testSchema);
}

Prevention

When it happens

Trigger: Calling conn.model('Test', otherSchema) twice with different schema objects on the same connection; most commonly triggered by hot-reload (Next.js/Nuxt dev, nodemon, Jest re-importing model files per test) or seed scripts re-running model definitions.

Common situations: Dev hot-reload re-executing model modules; test isolation redefining the same model per suite; duplicate model definitions spread across files; defining a model inside a request handler.

Related errors


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