Automattic/mongoose · error · MongooseError
Options must be an object, got "${options}"
Error message
Options must be an object, got "${options}" What it means
Query.prototype.setOptions() requires its options argument to be an object; null/undefined return early, and any other primitive (string, number, boolean) throws a MongooseError with the offending value interpolated into the message. Because every query helper that takes an options parameter (findOne, countDocuments, deleteOne, findOneAndUpdate, etc.) delegates to setOptions(), the error usually surfaces from the outer method call rather than an explicit setOptions() call.
Source
Thrown at lib/query.js:1743
*/
Query.prototype.setOptions = function(options, overwrite) {
// overwrite is only for internal use
if (overwrite) {
// ensure that _mongooseOptions & options are two different objects
this._mongooseOptions = (options && clone(options)) || {};
this.options = options || {};
if ('populate' in options) {
this.populate(this._mongooseOptions);
}
return this;
}
if (options == null) {
return this;
}
if (typeof options !== 'object') {
throw new MongooseError('Options must be an object, got "' + options + '"');
}
options = Object.assign({}, options);
if (Array.isArray(options.populate)) {
const populate = options.populate;
delete options.populate;
const _numPopulate = populate.length;
for (let i = 0; i < _numPopulate; ++i) {
this.populate(populate[i]);
}
}
if ('cloneUpdate' in options) {
this._mongooseOptions.cloneUpdate = options.cloneUpdate;
delete options.cloneUpdate;
}
if ('defaults' in options) {View on GitHub (pinned to 49cdab0136)
Solutions
- Pass a plain object: .findOne(filter, projection, { lean: true })
- JSON.parse() any serialized options string before passing it
- Re-check argument order — a primitive in the options slot usually means misaligned arguments (frequently a leftover callback or an extra leading parameter)
Example fix
// before
const doc = await Model.findOne({}, null, process.env.FIND_OPTS); // FIND_OPTS is the string '{"lean":true}'
// after
const doc = await Model.findOne({}, null, JSON.parse(process.env.FIND_OPTS)); Defensive patterns
Strategy: type-guard
Validate before calling
function toOptions(raw) {
if (raw == null) return undefined;
if (typeof raw === 'string') {
try { return JSON.parse(raw); } catch { return undefined; }
}
return typeof raw === 'object' ? raw : undefined;
}
const doc = await Model.findOne({}, null, toOptions(maybeOptions)); Type guard
const isOptionsObject = (v) => v == null || (typeof v === 'object' && v !== null);
Prevention
- Always pass options as a plain object literal
- JSON.parse strings coming from env vars, caches, or queues before use
- In TypeScript, type the options slot as a specific options interface, not any
When it happens
Trigger: .findOne(filter, projection, 'lean') instead of { lean: true }; .countDocuments({}, 10); .deleteOne({}, true); .setOptions('limit=5'); passing an unparsed JSON string pulled from an env var, cache, or message queue.
Common situations: Options serialized as strings (env vars, Redis, job queues) that were never JSON.parsed; argument positions shifted after removing a legacy callback during a Mongoose 7 upgrade; passing a URL query string instead of a parsed object.
Related errors
- sort() options argument must be an object or nullish
- Invalid addFields() argument. Must be an object
- Invalid select() argument. Must be string or object.
- sort() takes at most 2 arguments
- Invalid sort() argument, must be array of arrays
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/f06d11b9cb6036c1.
Report an issue: GitHub.