Automattic/mongoose · error · MongooseError

Model.find() no longer accepts a callback

Error message

Model.find() no longer accepts a callback

What it means

Model.find(conditions, projection, options) is the core query static; Mongoose 7 removed its callback form, so a function in any of the first four argument slots throws synchronously and no Query is returned. The result comes from awaiting the Query or calling .exec().

Source

Thrown at lib/model.js:2126

 *     await MyModel.find({ name: /john/i }, 'name friends').exec();
 *
 *     // passing options
 *     await MyModel.find({ name: /john/i }, null, { skip: 10 }).exec();
 *
 * @param {object|ObjectId} filter
 * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
 * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
 * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
 * @return {Query}
 * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
 * @see query casting https://mongoosejs.com/docs/tutorials/query_casting.html
 * @api public
 */

Model.find = function find(conditions, projection, options) {
  _checkContext(this, 'find');
  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') {
    throw new MongooseError('Model.find() no longer accepts a callback');
  }

  const mq = new this.Query({}, {}, this, this.$__collection);
  mq.select(projection);
  mq.setOptions(options);

  return mq.find(conditions);
};

/**
 * Finds a single document by its _id field. `findById(id)` is equivalent to `findOne({ _id: id })`.
 *
 * The `id` is cast based on the Schema before sending the command.
 *
 * This function triggers the following middleware.
 *
 * - `findOne()`
 *

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Await the query: `const users = await User.find({ age: { $gte: 18 } }, 'name');`
  2. Or `User.find({}).exec().then(...)`
  3. Automate the sweep: grep for `\.find\(.*=>` and `\.find\([^)]*,\s*function` and migrate all matches in one pass

Example fix

// before
User.find({ role: 'admin' }, (err, admins) => { if (err) throw err; ... });

// after
const admins = await User.find({ role: 'admin' });
Defensive patterns

Strategy: validation

Validate before calling

const isFn = (a) => typeof a === 'function';
if ([conditions, projection, options].some(isFn)) {
  throw new TypeError('find() is promise-only');
}
const docs = await User.find(conditions, projection, options);

Prevention

When it happens

Trigger: `User.find({ age: { $gte: 18 } }, 'name', (err, docs) => ...)`; `User.find(cb)`; any of the 4 positional slots receiving a function.

Common situations: The single most common hit when upgrading mongoose 6 to 7+: find() calls are ubiquitous; old tutorials and generated CRUD code are callback-based.

Related errors


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