Automattic/mongoose · error · MongooseError
`Model.${fnName}()` cannot run without a model as `this`. Ma
Error message
`Model.${fnName}()` cannot run without a model as `this`. Make sure you are not calling `new Model.${fnName}()` What it means
Second branch of `_checkContext`: the receiver is an object but lacks the internal `modelSymbol` property, which every real compiled mongoose Model carries. The canonical trigger is `new Model.someStatic()` — e.g. `new Model.discriminator()` — where `new` boxes the call on a fresh object without the symbol. The comment in the source says this check exists precisely because `new Model.discriminator()` otherwise produces an incomprehensible error.
Source
Thrown at lib/model.js:1070
}
return d;
};
/**
* Make sure `this` is a model
* @api private
*/
function _checkContext(ctx, fnName) {
// Check context, because it is easy to mistakenly type
// `new Model.discriminator()` and get an incomprehensible error
if (ctx == null || ctx === global) {
throw new MongooseError('`Model.' + fnName + '()` cannot run without a ' +
'model as `this`. Make sure you are calling `MyModel.' + fnName + '()` ' +
'where `MyModel` is a Mongoose model.');
} else if (ctx[modelSymbol] == null) {
throw new MongooseError('`Model.' + fnName + '()` cannot run without a ' +
'model as `this`. Make sure you are not calling ' +
'`new Model.' + fnName + '()`');
}
}
// Model (class) features
/*!
* Give the constructor the ability to emit events.
*/
for (const i in EventEmitter.prototype) {
Model[i] = EventEmitter.prototype[i];
}
/**
* This function is responsible for initializing the underlying connection in MongoDB based on schema options.
* This function performs the following operations:View on GitHub (pinned to 49cdab0136)
Solutions
- Remove `new`: `Event.discriminator('Click', schema)` returns the model directly
- Keep `new` only for the actual constructor: `const doc = new Event({...})`
- If wrapping statics, call them off the model: `MyModel.findOne(...)`
Example fix
// before
const Clicked = new Event.discriminator('Clicked', clickedSchema);
// after
const Clicked = Event.discriminator('Clicked', clickedSchema); Defensive patterns
Strategy: type-guard
Validate before calling
// Guard in dev builds: model statics must never be invoked with `new`
const safeStatic = (Model, fnName, ...args) => {
if (!isMongooseModel(Model)) throw new TypeError('Pass the model, not an instance/plain object');
return Model[fnName](...args); // note: no `new` on statics
}; Type guard
function isMongooseModel(v) {
return typeof v === 'function' &&
typeof v.modelName === 'string' &&
v.db != null &&
typeof v.findOne === 'function';
} Prevention
- Reserve `new` for documents (`new Model({...})`); statics like discriminator/init/syncIndexes never take `new`
- Prefer TypeScript: `new Model.init()` is a type error against the official mongoose types
- During review, search for `new \w+\.(discriminator|init|syncIndexes|findOne)` as a smell pattern
When it happens
Trigger: `const d = new Event.discriminator('Click', schema);`; `new User.init();`; any Model static invoked with `new` because the capitalized name reads like a class constructor; also calling a static with `.call(somePlainObject, ...)`.
Common situations: Copy-paste from code that uses classes with static factories; IDE autocompletion inserting `new` on capitalized methods; migrating from patterns like `new Model.findById()`. Note: only statics go through _checkContext — document methods (`new Model({...})`) are the legitimate constructor use.
Related errors
- `Model.${fnName}()` cannot run without a model as `this`. Ma
- Arguments must be aggregate pipeline operators
- Invalid addFields() argument. Must be an object
- Invalid project() argument. Must be string or object
- Aggregate `near()` must be called with non-nullish argument
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/33103692e01fea5c.
Report an issue: GitHub.