Automattic/mongoose · error · MongooseError

Model.syncIndexes() no longer accepts a callback

Error message

Model.syncIndexes() no longer accepts a callback

What it means

Model.syncIndexes() reconciles the indexes defined in the schema with those in MongoDB (creating missing ones, dropping/hiding stale ones) and returns a promise of dropped index names. Since Mongoose 7 it accepts only an options object; a function in either positional slot throws immediately.

Source

Thrown at lib/model.js:1332

 * performance. Before running `syncIndexes()`, you can use the [`diffIndexes()` function](#Model.diffIndexes())
 * to check what indexes `syncIndexes()` will drop and create.
 *
 * #### Example:
 *
 *     const { toDrop, toCreate } = await Model.diffIndexes();
 *     toDrop; // Array of strings containing names of indexes that `syncIndexes()` will drop
 *     toCreate; // Array of strings containing names of indexes that `syncIndexes()` will create
 *
 * @param {object} [options] options to pass to `ensureIndexes()`
 * @param {boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher
 * @return {Promise}
 * @api public
 */

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

  const autoCreate = options?.autoCreate ??
    this.schema.options?.autoCreate ??
    this.db.config.autoCreate ??
    this.db.base?.options?.autoCreate ??
    true;

  if (autoCreate) {
    try {
      await this.createCollection();
    } catch (err) {
      if (err != null && (err.name !== 'MongoServerError' || err.code !== 48)) {
        throw err;
      }
    }
  }

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Await it: `const dropped = await User.syncIndexes();`
  2. For many models: `await Promise.all(mongoose.modelNames().map(n => mongoose.model(n).syncIndexes()))`
  3. Check `Model.diffIndexes()` first if you only want to preview changes without the misleading callback error

Example fix

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

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

Strategy: validation

Validate before calling

const isFn = (a) => typeof a === 'function';
if (isFn(options) || isFn(arguments[1])) throw new TypeError('syncIndexes() is promise-only');
const dropped = await User.syncIndexes(options);

Prevention

When it happens

Trigger: `User.syncIndexes({ hideIndexes: true }, cb)` or `User.syncIndexes(cb)`; deployment scripts from the callback era that call syncIndexes per model in a loop with a callback.

Common situations: Mongoose 6-to-7 upgrades; index-sync utilities iterated with async.forEach-style callback helpers from older libraries.

Related errors


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