Automattic/mongoose · error · MongooseError

Model.cleanIndexes() no longer accepts a callback

Error message

Model.cleanIndexes() no longer accepts a callback

What it means

Model.cleanIndexes() drops (or hides, with `hideIndexes: true`) indexes that exist in MongoDB but not in the schema, returning the affected index names. Mongoose 7 removed its callback form, so a function in either argument slot throws before diffing starts.

Source

Thrown at lib/model.js:1573

  return toDrop;
}
/**
 * Deletes all indexes that aren't defined in this model's schema. Used by
 * `syncIndexes()`.
 *
 * The returned promise resolves to a list of the dropped indexes' names as an array
 *
 * @param {object} [options]
 * @param {string[]} [options.toDrop] if specified, contains a list of index names to drop
 * @param {boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher
 * @return {Promise<string[]>} list of dropped or hidden index names
 * @api public
 */

Model.cleanIndexes = async function cleanIndexes(options) {
  _checkContext(this, 'cleanIndexes');
  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') {
    throw new MongooseError('Model.cleanIndexes() no longer accepts a callback');
  }
  const model = this;

  if (Array.isArray(options?.toDrop)) {
    const res = await _dropIndexes(options.toDrop, model, options);
    return res;
  }

  const res = await model.diffIndexes();
  return await _dropIndexes(res.toDrop, model, options);
};

async function _dropIndexes(toDrop, model, options) {
  if (toDrop.length === 0) {
    return [];
  }

  const collection = model.$__collection;

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Await it: `const dropped = await User.cleanIndexes();`
  2. Pass a precomputed list if you orchestrate externally: `await User.cleanIndexes({ toDrop: ['name_1'] })`
  3. Batch over models with Promise.all instead of callback counters

Example fix

// before
User.cleanIndexes(function(err, dropped) { ... });

// after
const dropped = await User.cleanIndexes();
Defensive patterns

Strategy: validation

Validate before calling

const isFn = (a) => typeof a === 'function';
if (isFn(options)) throw new TypeError('cleanIndexes() is promise-only');
const dropped = await User.cleanIndexes(options);

Prevention

When it happens

Trigger: `User.cleanIndexes(cb)`; `User.cleanIndexes({ hideIndexes: true }, cb)`; teardown/maintenance scripts written against Mongoose 5/6.

Common situations: Apps upgrading mongoose majors while keeping old maintenance scripts; scripts that clean indexes for several models with a shared callback.

Related errors


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