{"record":{"id":"5bc46d13f7664aca","repo":"Automattic/mongoose","slug":"must-provide-a-filter-object","errorCode":null,"errorMessage":"Must provide a filter object.","messagePattern":"Must provide a filter object\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/helpers/model/castBulkWrite.js","lineNumber":108,"sourceCode":"    doc.$session(options.session);\n  }\n  const versionKey = model?.schema?.options?.versionKey;\n  if (versionKey && doc[versionKey] == null) {\n    doc[versionKey] = 0;\n  }\n  insertOne['document'] = doc;\n\n  if (options.skipValidation || insertOne.skipValidation) {\n    return insertOne;\n  }\n\n  await insertOne['document'].$validate();\n  return insertOne;\n};\n\nmodule.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne, options, now) {\n  if (!updateOne['filter']) {\n    throw new Error('Must provide a filter object.');\n  }\n  if (!updateOne['update']) {\n    throw new Error('Must provide an update object.');\n  }\n\n  const model = decideModelByObject(originalModel, updateOne['filter']);\n  const schema = model.schema;\n  const strict = options.strict ?? model.schema.options.strict;\n\n  const update = clone(updateOne['update']);\n\n  _addDiscriminatorToObject(schema, updateOne['filter']);\n\n  const doInitTimestamps = getTimestampsOpt(updateOne, options);\n\n  if (model.schema.$timestamps != null && doInitTimestamps) {\n    const createdAt = model.schema.$timestamps.createdAt;\n    const updatedAt = model.schema.$timestamps.updatedAt;","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/helpers/model/castBulkWrite.js#L90-L126","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait Model.bulkWrite(rows.map(r => ({\n  updateOne: { update: { $set: { status: r.status } } } // filter missing\n})));\n\n// after\nawait Model.bulkWrite(rows.map(r => ({\n  updateOne: { filter: { _id: r._id }, update: { $set: { status: r.status } } }\n})));","handlingStrategy":"validation","validationCode":"function normalizeBulkOps(ops) {\n  return ops.map(op => {\n    if (op.updateOne != null && op.updateOne.filter == null) {\n      throw new Error(`updateOne missing filter: ${JSON.stringify(op).slice(0, 200)}`);\n    }\n    return op;\n  });\n}\nawait Model.bulkWrite(normalizeBulkOps(ops));","typeGuard":"function isCompleteUpdateOne(op) {\n  return op?.updateOne?.filter != null && op.updateOne.update != null;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["mongoose","bulkwrite","updateone","filter","validation"],"backgroundTag":"missing-query-filter","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}