Automattic/mongoose · error · TypeError
2nd argument to `Model` constructor must be a POJO or string
Error message
2nd argument to `Model` constructor must be a POJO or string, **not** a schema. Make sure you're calling `mongoose.model()`, not `mongoose.Model()`.
What it means
Mongoose's base Model constructor takes (doc, fields, options) — never a Schema. This TypeError guards against the classic mistake of trying to compile or instantiate a model via the Model constructor directly with a schema (new mongoose.Model(doc, schema)). Model classes must be compiled through mongoose.model(name, schema) (or connection.model(...)); only the compiled class is instantiated with documents.
Source
Thrown at lib/model.js:134
* // You also use a model to create queries:
* const userFromDb = await UserModel.findOne({ name: 'Foo' });
*
* @param {object} doc values for initial set
* @param {object} [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projection](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()).
* @param {object} [options] optional object containing the options for the document.
* @param {boolean} [options.defaults=true] if `false`, skip applying default values to this document.
* @param {boolean} [options.skipId=false] By default, Mongoose document if one is not provided and the document's schema does not override Mongoose's default `_id`. Set `skipId` to `true` to skip this generation step.
* @inherits Document https://mongoosejs.com/docs/api/document.html
* @event `error`: If listening to this event, 'error' is emitted when a document was saved and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
* @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
* @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event.
* @event `index-single-done`: Emitted when an individual index finishes within `Model#ensureIndexes`. If an error occurred it is passed with the event. The fields, options, and index name are also passed.
* @api public
*/
function Model(doc, fields, options) {
if (fields instanceof Schema) {
throw new TypeError('2nd argument to `Model` constructor must be a POJO or string, ' +
'**not** a schema. Make sure you\'re calling `mongoose.model()`, not ' +
'`mongoose.Model()`.');
}
if (typeof doc === 'string') {
throw new TypeError('First argument to `Model` constructor must be an object, ' +
'**not** a string. Make sure you\'re calling `mongoose.model()`, not ' +
'`mongoose.Model()`.');
}
Document.call(this, doc, fields, options);
}
/**
* Inherits from Document.
*
* All Model.prototype features are available on
* top level (non-sub) documents.
* @api private
*/View on GitHub (pinned to 49cdab0136)
Solutions
- Compile the model first: const User = mongoose.model('User', userSchema)
- Instantiate with plain documents: new User({ name: 'x' })
- For multiple connections use connection.model('User', userSchema)
Example fix
// before
const User = new mongoose.Model({}, userSchema); // TypeError
// after
const User = mongoose.model('User', userSchema);
const doc = new User({ name: 'x' }); Defensive patterns
Strategy: type-guard
Validate before calling
// Compile models through the registry only
function compileModel(name, schema) {
if (!(schema instanceof mongoose.Schema)) throw new Error('Expected a mongoose.Schema');
return mongoose.model(name, schema);
} Type guard
const isModelConstructorCallSafe = (fields) => !(fields instanceof mongoose.Schema);
Try / catch
try {
new SomeModel(doc);
} catch (err) {
if (err instanceof TypeError && /2nd argument to `Model`/.test(err.message)) {
// you passed a schema: switch to mongoose.model(name, schema)
} else throw err;
} Prevention
- Always compile with mongoose.model()/connection.model() and instantiate only the returned class
- Reserve `import { Model }` for type positions (extends Model<T>), never for construction
- Lint for `new mongoose.Model` and `Model\(` direct calls in code review
When it happens
Trigger: const User = new mongoose.Model({}, userSchema), new SomeModel(doc, someSchema), or class Foo extends mongoose.Model {} invoked as new Foo(schema). Usually copy-paste or autocomplete substituting mongoose.Model for mongoose.model.
Common situations: Misreading docs and treating Model as a factory; TypeScript codemods rewriting mongoose.model into new Model; old code that subclassed Model directly; editor autocomplete picking Model over model.
Related errors
- First argument to `Model` constructor must be an object, **n
- The 2nd parameter to `mongoose.model()` should be a schema o
- Aggregate `near()` must be called with non-nullish argument
- Invalid sort() argument. Must be a string or object.
- If thenExpr or elseExpr is string, it must be either $$DESCE
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/9cd70fddbfd52f86.
Report an issue: GitHub.