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

  1. Compile the model first: const User = mongoose.model('User', userSchema)
  2. Instantiate with plain documents: new User({ name: 'x' })
  3. 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

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


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