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
- Pass the option: .findOneAndUpdate(filter, pipeline, { updatePipeline: true, returnDocument: 'after' })
- Or enable it globally with mongoose.set('updatePipeline', true) if most updates are pipelines
- 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
- Always set updatePipeline when the update is an aggregation array
- Wrap update helpers in a small adapter that infers the flag automatically
- Use classic { $inc, $set } object updates when pipeline operators aren't needed
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
- Invalid atomic update value for ${op}. Expected an object, r
- Model.findOneAndUpdate() no longer accepts a callback
- Options must be an object, got "${options}"
- sort() options argument must be an object or nullish
- Invalid addFields() argument. Must be an object
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/61591f1f1e4f5df4.
Report an issue: GitHub.