Automattic/mongoose · error · MongooseError

Cannot mix array and object updates (current: ${_previewUpda

Error message

Cannot mix array and object updates (current: ${_previewUpdate(this[queryUpdateSymbol])}, incoming: ${_previewUpdate(update[queryUpdateSymbol])})

What it means

Thrown by Query.prototype._mergeUpdate when the query's stored update is an array (a pipeline update) but you merge in a Query object whose own update is a plain object. Mongoose cannot concatenate a pipeline stage list with an operator object like {$set: ...}, so it fails fast with a preview of both values instead of producing a corrupt update.

Source

Thrown at lib/query.js:4128

Query.prototype._mergeUpdate = function(update) {
  _cloneUpdateIfShared(this);

  const updatePipeline = this._mongooseOptions.updatePipeline;
  if (!updatePipeline && Array.isArray(update)) {
    throw new MongooseError('Cannot pass an array to query updates unless the `updatePipeline` option is set.');
  }
  if (!this[queryUpdateSymbol]) {
    this[queryUpdateSymbol] = Array.isArray(update) ? [] : {};
  }

  if (update == null || (typeof update === 'object' && utils.hasOwnKeys(update) === false)) {
    return;
  }

  if (update instanceof Query) {
    if (Array.isArray(this[queryUpdateSymbol])) {
      throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this[queryUpdateSymbol])}, incoming: ${_previewUpdate(update[queryUpdateSymbol])})`);
    }
    if (update[queryUpdateSymbol]) {
      utils.mergeClone(this[queryUpdateSymbol], update[queryUpdateSymbol]);
    }
  } else if (Array.isArray(update)) {
    if (!Array.isArray(this[queryUpdateSymbol])) {
      // `_update` may be empty object by default, like in `doc.updateOne()`
      // because we create the query first, then run hooks, then apply the update.
      if (this[queryUpdateSymbol] == null || utils.isEmptyObject(this[queryUpdateSymbol])) {
        this[queryUpdateSymbol] = [];
      } else {
        throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this[queryUpdateSymbol])}, incoming: ${_previewUpdate(update)})`);
      }
    }
    this[queryUpdateSymbol] = this[queryUpdateSymbol].concat(update);
  } else {
    if (Array.isArray(this[queryUpdateSymbol])) {
      throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this[queryUpdateSymbol])}, incoming: ${_previewUpdate(update)})`);

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Pick one update shape per query: convert the object update to an equivalent pipeline ([{$set: {...}}] with `updatePipeline: true`) or keep both as plain objects.
  2. Do not merge Query objects with different update kinds; execute them as separate updateOne/updateMany statements.
  3. Build a fresh Query per update instead of re-using and merging into one instance.

Example fix

// before
const q = Model.updateOne({}, [{ $set: { a: 1 } }], { updatePipeline: true });
const q2 = Model.updateOne({}, { $set: { b: 2 } });
q.updateOne({}, q2); // throws: Cannot mix array and object updates

// after
await Model.updateOne({}, [{ $set: { a: 1 } }], { updatePipeline: true });
await Model.updateOne({}, { $set: { b: 2 } });
Defensive patterns

Strategy: validation

Validate before calling

function assertSameUpdateKind(query, incoming) {
  const currentIsArray = Array.isArray(query.getUpdate());
  const incomingIsArray = incoming instanceof mongoose.Query ? Array.isArray(incoming.getUpdate()) : Array.isArray(incoming);
  if (currentIsArray !== incomingIsArray) {
    throw new Error('Refusing to merge array and object updates onto one query');
  }
}

Type guard

const updatesAreCompatible = (a, b) => Array.isArray(a) === Array.isArray(b);

Try / catch

try { q.updateOne({}, incoming); } catch (err) { if (err instanceof mongoose.Error && /Cannot mix array and object updates/.test(err.message)) { /* run as separate statements instead */ } throw err; }

Prevention

When it happens

Trigger: `const q = Model.updateOne({}, [{$set: {a: 1}}], { updatePipeline: true }); q.updateOne({}, q2)` where q2 is a Query built with an object update, or any code path that calls query.merge(otherQuery) with mismatched update shapes.

Common situations: Helper functions that accept either an update object or a Query and chain them onto one builder query; refactoring a query builder so some branches use pipeline updates and others use operator objects; test fixtures that reuse and merge Query objects.

Related errors


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