{"record":{"id":"41891d6f0c38ec6a","repo":"mongodb/node-mongodb-native","slug":"query-filter-must-be-a-plain-object-or-objectid","errorCode":null,"errorMessage":"Query filter must be a plain object or ObjectId","messagePattern":"Query filter must be a plain object or ObjectId","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/operations/find.ts","lineNumber":101,"sourceCode":"  /**\n   * @remarks WriteConcern can still be present on the options because\n   * we inherit options from the client/db/collection.  The\n   * key must be present on the options in order to delete it.\n   * This allows typescript to delete the key but will\n   * not allow a writeConcern to be assigned as a property on options.\n   */\n  override options: FindOptions & { writeConcern?: never };\n  filter: Document;\n\n  constructor(ns: MongoDBNamespace, filter: Document = {}, options: FindOptions = {}) {\n    super(undefined, options);\n\n    this.options = { ...options };\n    delete this.options.writeConcern;\n    this.ns = ns;\n\n    if (typeof filter !== 'object' || Array.isArray(filter)) {\n      throw new MongoInvalidArgumentError('Query filter must be a plain object or ObjectId');\n    }\n\n    // special case passing in an ObjectId as a filter\n    this.filter = filter != null && filter._bsontype === 'ObjectId' ? { _id: filter } : filter;\n\n    this.SERVER_COMMAND_RESPONSE_TYPE = this.explain ? ExplainedCursorResponse : CursorResponse;\n  }\n\n  override get commandName() {\n    return 'find' as const;\n  }\n\n  override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {\n    return {\n      ...this.options,\n      ...this.bsonOptions,\n      documentsReturnedIn: 'firstBatch',\n      session: this.session,","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/operations/find.ts#L83-L119","documentation":"Thrown as a MongoInvalidArgumentError by the FindOperation constructor when the filter argument is not an object or is an array. The find command requires a plain query document (a BSON object); arrays, primitives, and null are rejected. An ObjectId is also accepted because the constructor special-cases it into {_id: ObjectId}.","triggerScenarios":"Calling collection.find(), collection.findOne(), or any path that builds a FindOperation with a filter that is a string, number, boolean, array, or undefined that bypassed TypeScript types (e.g. from untyped JSON input). The check at find.ts:100 is `typeof filter !== 'object' || Array.isArray(filter)`.","commonSituations":"Passing a raw id string instead of an ObjectId or {_id: ...}; deserializing a filter from JSON where an array was supplied; dynamically building a filter variable that ends up as an array of conditions instead of an object.","solutions":["Pass a plain object as the filter, e.g. collection.find({ status: 'active' }).","To query by _id, pass an ObjectId: collection.find(new ObjectId(id)) or collection.find({ _id: new ObjectId(id) }).","Coerce dynamic filter input to a plain object before calling find, rejecting arrays explicitly.","If you intended an $or query, wrap conditions in { $or: [...] } rather than passing the bare array."],"exampleFix":"// before\nconst doc = await collection.findOne('64abc...'); // string filter -> error\n\n// after\nconst { ObjectId } = require('bson');\nconst doc = await collection.findOne({ _id: new ObjectId('64abc...') });","handlingStrategy":"type-guard","validationCode":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\nif (!isPlainObject(filter) && !(filter instanceof ObjectId)) {\n  throw new TypeError('filter must be a plain object or ObjectId');\n}\nawait collection.find(filter as any).toArray();","typeGuard":"function isFindFilter(v: unknown): v is Record<string, unknown> | ObjectId {\n  if (v instanceof ObjectId) return true;\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Always pass a plain object or an ObjectId as the find filter.","Wrap incoming JSON filter input with a type guard before querying.","Use { _id: new ObjectId(id) } rather than a raw string when targeting by id."],"tags":["validation","find","filter","argument","typescript"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}