Automattic/mongoose · error · MongooseError
Query must have `op` before executing
Error message
Query must have `op` before executing
What it means
Query.prototype.exec() requires that the query already knows which operation to run; `this.op` is set by find(), findOne(), updateOne(), countDocuments(), etc. If exec() runs on a query with no operation, Mongoose throws this error because there is nothing meaningful to send to the server.
Source
Thrown at lib/query.js:4755
* const promise = query.exec('update');
*
* @param {string|Function} [operation]
* @return {Promise}
* @api public
*/
Query.prototype.exec = async function exec(op) {
if (typeof op === 'function' || (arguments.length >= 2 && typeof arguments[1] === 'function')) {
throw new MongooseError('Query.prototype.exec() no longer accepts a callback');
}
this._validateOp();
if (typeof op === 'string') {
this.op = op;
}
if (this.op == null) {
throw new MongooseError('Query must have `op` before executing');
}
if (this.model == null) {
throw new MongooseError('Query must have an associated model before executing');
}
const thunk = opToThunk.get(this.op);
if (!thunk) {
throw new MongooseError('Query has invalid `op`: "' + this.op + '"');
}
if (this.options?.sort && typeof this.options.sort === 'object' && Object.hasOwn(this.options.sort, '')) {
throw new MongooseError('Invalid field "" passed to sort()');
}
if (this._execCount > 0) {
let str = this.toString();
if (str.length > 60) {
str = str.slice(0, 60) + '...';View on GitHub (pinned to 49cdab0136)
Solutions
- Add the operation before exec: `Model.find({ name: 'x' }).exec()` or `Model.where('name', 'x').find().exec()`.
- Or pass the op string to exec if the query has conditions but no op: `query.exec('find')`.
- Audit builder functions to guarantee every code path ends in find()/findOne()/updateOne()/etc.
Example fix
// before
const docs = await Model.where('age').gte(18).exec(); // throws: Query must have `op` before executing
// after
const docs = await Model.where('age').gte(18).find().exec(); Defensive patterns
Strategy: validation
Validate before calling
function execQuery(query) {
if (query.op == null) query.find(); // or throw: conditions without an operation
return query.exec();
} Type guard
const queryHasOp = (q) => q.op != null;
Try / catch
try { await q.exec(); } catch (err) { if (err instanceof mongoose.Error && /must have `op`/.test(err.message)) { q.find(); return q.exec(); } throw err; } Prevention
- End every builder chain with find()/findOne()/updateOne().
- Do not treat where()/sort()/limit() as executable terminals.
- Unit-test builder helpers to assert q.op is set before exec.
When it happens
Trigger: `Model.where('name', 'x').exec()` (where() only adds a condition, it does not set an op); `new Model.Query().exec()`; exec('') with an empty string; building conditions via query helpers and forgetting the terminal find/findOne call.
Common situations: Query-builder helpers that chain where()/sort()/limit() and accidentally return before calling find(); dynamically skipping the operation-setting step because of a falsy branch; copy-paste that drops the `.find()`.
Related errors
- Query must have an associated model before executing
- Query has invalid `op`: "${this.op}"
- Invalid addFields() argument. Must be an object
- Invalid arguments
- Aggregate.prototype.exec() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/07c4953a9d11ecf7.
Report an issue: GitHub.