Automattic/mongoose · error · Error
Must provide a filter object.
Error message
Must provide a filter object.
What it means
During Model.bulkWrite(), Mongoose casts each operation; castUpdateOne (lib/helpers/model/castBulkWrite.js) requires every updateOne op to carry both a filter and an update. A missing or falsy filter (undefined, null, omitted key, or a typo'd key like query or where) throws Error('Must provide a filter object.') before anything is sent to the server.
Source
Thrown at lib/helpers/model/castBulkWrite.js:108
doc.$session(options.session);
}
const versionKey = model?.schema?.options?.versionKey;
if (versionKey && doc[versionKey] == null) {
doc[versionKey] = 0;
}
insertOne['document'] = doc;
if (options.skipValidation || insertOne.skipValidation) {
return insertOne;
}
await insertOne['document'].$validate();
return insertOne;
};
module.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne, options, now) {
if (!updateOne['filter']) {
throw new Error('Must provide a filter object.');
}
if (!updateOne['update']) {
throw new Error('Must provide an update object.');
}
const model = decideModelByObject(originalModel, updateOne['filter']);
const schema = model.schema;
const strict = options.strict ?? model.schema.options.strict;
const update = clone(updateOne['update']);
_addDiscriminatorToObject(schema, updateOne['filter']);
const doInitTimestamps = getTimestampsOpt(updateOne, options);
if (model.schema.$timestamps != null && doInitTimestamps) {
const createdAt = model.schema.$timestamps.createdAt;
const updatedAt = model.schema.$timestamps.updatedAt;View on GitHub (pinned to 49cdab0136)
Solutions
- Ensure every updateOne op has a filter, typically { filter: { _id: row._id }, update: { $set: ... } }.
- Validate and normalize the ops array before calling bulkWrite (see validation code).
- If you meant to update many documents, use the updateMany op or Model.updateMany() with an explicit filter - never an accidentally empty one.
Example fix
// before
await Model.bulkWrite(rows.map(r => ({
updateOne: { update: { $set: { status: r.status } } } // filter missing
})));
// after
await Model.bulkWrite(rows.map(r => ({
updateOne: { filter: { _id: r._id }, update: { $set: { status: r.status } } }
}))); Defensive patterns
Strategy: validation
Validate before calling
function normalizeBulkOps(ops) {
return ops.map(op => {
if (op.updateOne != null && op.updateOne.filter == null) {
throw new Error(`updateOne missing filter: ${JSON.stringify(op).slice(0, 200)}`);
}
return op;
});
}
await Model.bulkWrite(normalizeBulkOps(ops)); Type guard
function isCompleteUpdateOne(op) {
return op?.updateOne?.filter != null && op.updateOne.update != null;
} Prevention
- Type your ops arrays (TypeScript: mongoose.AnyBulkWriteOperation) so missing keys fail at compile time.
- Build bulk ops with a small factory (e.g. updateOneBy(id, set)) that always sets filter.
- Filter out and log malformed rows instead of letting one bad op abort the whole batch.
When it happens
Trigger: Model.bulkWrite([{ updateOne: { update: { $set: { status: 1 } } } } ]) with no filter key; dynamically built ops where filter is undefined for some rows; { updateOne: { filter: null, update } }; a refactor renamed the filter field.
Common situations: ETL scripts assembling bulk ops from CSV or API rows where some rows lack an id; partial spreads like { ...base, update } accidentally dropping filter; copying op shapes from updateMany examples.
Related errors
- Query filter must be an object, got an array ${util.inspect(
- bulkWrite failed with ${validationErrors.length} Mongoose va
- ${operation} failed with ${validationErrors.length} Mongoose
- Arguments must be aggregate pipeline operators
- Aggregate `near()` argument must have a `near` property
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/5bc46d13f7664aca.
Report an issue: GitHub.