Automattic/mongoose · error · MongooseError

Model.prototype.deleteOne() no longer accepts a callback

Error message

Model.prototype.deleteOne() no longer accepts a callback

What it means

Like save(), Model.prototype.deleteOne() lost its callback form in Mongoose 7: it takes only an options object and returns a Query (thenable). Passing a function as the first or second argument throws this MongooseError immediately, guarding against a silently-never-invoked callback.

Source

Thrown at lib/model.js:819

 *
 *     await product.deleteOne();
 *     await Product.findById(product._id); // null
 *
 * Since `deleteOne()` returns a Query, the `deleteOne()` will **not** execute unless you use either `await`, `.then()`, `.catch()`, or [`.exec()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.exec())
 *
 * #### Example:
 *
 *     product.deleteOne(); // Doesn't do anything
 *     product.deleteOne().exec(); // Deletes the document, returns a promise
 *
 * @return {Query} Query
 * @api public
 */

Model.prototype.deleteOne = function deleteOne(options) {
  if (typeof options === 'function' ||
      typeof arguments[1] === 'function') {
    throw new MongooseError('Model.prototype.deleteOne() no longer accepts a callback');
  }

  if (!options) {
    options = {};
  }

  if (Object.hasOwn(options, 'session')) {
    this.$session(options.session);
  }

  const self = this;
  const where = this.$__where();
  const query = self.constructor.deleteOne();

  if (this.$session() != null) {
    if (!('session' in query.options)) {
      query.options.session = this.$session();
    }

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Use await doc.deleteOne() (optionally with .exec())
  2. Promisify at the API boundary if external consumers still expect callbacks: doc.deleteOne(opts).then(r => cb(null, r), cb)
  3. Grep for `.deleteOne(function` and `.deleteOne(.*, *cb` during the migration

Example fix

// before
product.deleteOne(function (err) {
  if (err) return next(err);
  res.end();
});

// after
await product.deleteOne();
res.end();
Defensive patterns

Strategy: validation

Validate before calling

// Guard a shared helper against callback-style deleteOne
async function removeDoc(doc, options) {
  if (typeof options === 'function' || typeof arguments[2] === 'function') {
    throw new Error('deleteOne() is promise-based — await it instead');
  }
  return doc.deleteOne(options);
}

Type guard

const isCallback = (x) => typeof x === 'function';

Try / catch

try {
  await doc.deleteOne({ session });
} catch (err) {
  // handle normally; no callback path exists
}

Prevention

When it happens

Trigger: doc.deleteOne(function (err) { ... }) or doc.deleteOne({ session }, cb) after upgrading to Mongoose 7+.

Common situations: Mongoose 6-to-7 migrations; shared CRUD helpers with callback signatures; older Stack Overflow snippets pasted into modern code.

Related errors


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