Automattic/mongoose · warning · TypeError

Invalid sort() argument. Must be a string or object.

Error message

Invalid sort() argument. Must be a string or object.

What it means

While building indexes for a model (Model.init() / ensureIndexes during compile or model.syncIndexes()), Mongoose inspects each index spec; MongoDB always creates a unique index on _id and does not allow a second, different index on the same key. isDefaultIdIndex() detects specs that try to redefine the _id index (e.g. { _id: 1 } or {_id: 1} with extra options), and Mongoose warns that the custom definition will not be applied as expected.

Source

Thrown at lib/aggregate.js:677

      if (arg[field] instanceof Object && arg[field].$meta) {
        sort[field] = arg[field];
        return;
      }
      sort[field] = desc.indexOf(arg[field]) === -1 ? 1 : -1;
    });
  } else if (arguments.length === 1 && typeof arg === 'string') {
    arg.split(/\s+/).forEach(function(field) {
      if (!field) {
        return;
      }
      const ascend = field[0] === '-' ? -1 : 1;
      if (ascend === -1) {
        field = field.substring(1);
      }
      sort[field] = ascend;
    });
  } else {
    throw new TypeError('Invalid sort() argument. Must be a string or object.');
  }

  return this.append({ $sort: sort });
};

/**
 * Appends new $unionWith operator to this aggregate pipeline.
 *
 * #### Example:
 *
 *     aggregate.unionWith({ coll: 'users', pipeline: [ { $match: { _id: 1 } } ] });
 *
 * @see $unionWith https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith
 * @param {object} options to $unionWith query as described in the above link
 * @return {Aggregate}
 * @api public
 */

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Remove the _id entry from schema.index() calls — MongoDB guarantees the _id unique index automatically.
  2. Remove index: true from the _id field definition: { _id: Schema.Types.ObjectId } is enough.
  3. If you need a custom _id type (e.g. Number), that is supported — just do not also declare an index on it.
  4. Run model.syncIndexes() after cleaning up and confirm via db.collection.getIndexes() that only one _id index exists.

Example fix

// before
const schema = new Schema({ _id: { type: ObjectId, index: true } });
schema.index({ _id: 1 }, { name: 'custom_id' });

// after
const schema = new Schema({ _id: ObjectId }); // default unique index is automatic
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeIndexes(schema) {
  const json = schema.indexes(); // [[fields, options], ...]
  for (const [fields] of json) {
    const keys = Object.keys(fields);
    if (keys.length === 1 && keys[0] === '_id') {
      throw new Error('Remove custom index on _id; MongoDB creates it automatically');
    }
  }
}

Prevention

When it happens

Trigger: schema.index({ _id: 1 }) or schema.index({ _id: 1 }, { unique: true, name: 'custom' }); defining { _id: { type: Schema.Types.ObjectId, index: true } } in the schema; setting sparse/expireAfterSeconds options on an _id index spec.

Common situations: Auto-generated index definitions from tooling that indexes every field; teams copying an index list from mongo shell explain output back into the schema; migration scripts that materialize existing DB indexes as schema.index() calls including the implicit _id one.

Related errors


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