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
- Reuse-or-define guard: const Test = mongoose.models.Test ?? mongoose.model('Test', schema)
- Reuse the compiled model instead: const Test = conn.model('Test') (name-only lookup)
- Delete before redefining: conn.deleteModel('Test')
- 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
- Use the reuse-or-define guard: mongoose.models.X ?? mongoose.model('X', schema)
- Define each model exactly once per process in a dedicated module
- Call conn.deleteModel(name) in test teardown before redefining models
- Only enable overwriteModels (mongoose.set('overwriteModels', true)) when redefinition is intentional
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
- `connection.model()` should not be run with `new`. If you ar
- The 2nd parameter to `mongoose.model()` should be a schema o
- Schema hasn't been registered for model "${name}". Use mongo
- First parameter to `deleteModel()` must be a string or regex
- 2nd argument to `Model` constructor must be a POJO or string
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/cfa120f9f9003216.
Report an issue: GitHub.