Automattic/mongoose · error · MongooseError

Cannot pass an array to query updates unless the `updatePipe

Error message

Cannot pass an array to query updates unless the `updatePipeline` option is set.

What it means

findOneAndUpdate treats an array in the update slot as an aggregation pipeline only when the updatePipeline option is enabled — per call via { updatePipeline: true } or globally via mongoose.set('updatePipeline', true). Without the flag, an array update throws immediately because classic update documents must be plain objects; this differs from the raw MongoDB driver, where array updates are always pipelines.

Source

Thrown at lib/query.js:3540

  }

  const globalReturnDocument = this?.model?.base?.options?.returnDocument;
  const globalReturnOriginal = this?.model?.base?.options?.returnOriginal;
  if (options.new == null && options.returnDocument == null && options.returnOriginal == null) {
    if (globalReturnDocument != null) {
      options.returnDocument = globalReturnDocument;
    } else if (globalReturnOriginal != null) {
      options.returnOriginal = globalReturnOriginal;
    }
  }

  const updatePipeline = this?.model?.base?.options?.updatePipeline;
  if (options.updatePipeline == null && updatePipeline != null) {
    options.updatePipeline = updatePipeline;
  }

  if (!options.updatePipeline && Array.isArray(update)) {
    throw new MongooseError('Cannot pass an array to query updates unless the `updatePipeline` option is set.');
  }

  this.setOptions(options);

  // apply doc
  if (update) {
    if (this[queryUpdateSymbol] == null || utils.isEmptyObject(this[queryUpdateSymbol])) {
      this[queryUpdateSymbol] = update;
      this._updateIsShared = true;
    } else {
      this._mergeUpdate(update);
    }
  }

  return this;
};

/**

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Pass the option: .findOneAndUpdate(filter, pipeline, { updatePipeline: true, returnDocument: 'after' })
  2. Or enable it globally with mongoose.set('updatePipeline', true) if most updates are pipelines
  3. If no pipeline operators are needed, use a classic object update instead: { $inc: { count: 1 } }

Example fix

// before
const doc = await Model.findOneAndUpdate(
  { _id },
  [{ $set: { count: { $add: ['$count', 1] } } }]
);

// after
const doc = await Model.findOneAndUpdate(
  { _id },
  [{ $set: { count: { $add: ['$count', 1] } } }],
  { updatePipeline: true, returnDocument: 'after' }
);
Defensive patterns

Strategy: validation

Validate before calling

// Infer the flag so array updates always carry updatePipeline
const opts = { ...baseOptions, ...(Array.isArray(update) ? { updatePipeline: true } : null) };
const doc = await Model.findOneAndUpdate(filter, update, opts);

Type guard

const isPipelineUpdate = (u) => Array.isArray(u);

Prevention

When it happens

Trigger: .findOneAndUpdate({ _id }, [{ $set: { count: { $add: ['$count', 1] } } }]) with no options; copying native-driver pipeline updates into Mongoose; using pipeline-only operators ($add, $cond, $subtract) inside $set.

Common situations: Atomic math beyond $inc (e.g. multiply two fields, conditional updates); migrating from the MongoDB node driver; tutorials written against driver syntax.

Related errors


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