{"record":{"id":"4113f1e13259dda7","repo":"Automattic/mongoose","slug":"query-has-invalid-op-this-op","errorCode":null,"errorMessage":"Query has invalid `op`: \"${this.op}\"","messagePattern":"Query has invalid `op`: \"(.+?)\"","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/query.js","lineNumber":4763,"sourceCode":"  if (typeof op === 'function' || (arguments.length >= 2 && typeof arguments[1] === 'function')) {\n    throw new MongooseError('Query.prototype.exec() no longer accepts a callback');\n  }\n\n  this._validateOp();\n  if (typeof op === 'string') {\n    this.op = op;\n  }\n\n  if (this.op == null) {\n    throw new MongooseError('Query must have `op` before executing');\n  }\n  if (this.model == null) {\n    throw new MongooseError('Query must have an associated model before executing');\n  }\n\n  const thunk = opToThunk.get(this.op);\n  if (!thunk) {\n    throw new MongooseError('Query has invalid `op`: \"' + this.op + '\"');\n  }\n\n  if (this.options?.sort && typeof this.options.sort === 'object' && Object.hasOwn(this.options.sort, '')) {\n    throw new MongooseError('Invalid field \"\" passed to sort()');\n  }\n\n  if (this._execCount > 0) {\n    let str = this.toString();\n    if (str.length > 60) {\n      str = str.slice(0, 60) + '...';\n    }\n    throw new MongooseError('Query was already executed: ' + str);\n  }\n  this._execCount++;\n\n  const _this = this;\n  return traceQuery(async function maybeTracedQueryExec() {\n    let skipWrappedFunction = null;","sourceCodeStart":4745,"sourceCodeEnd":4781,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/query.js#L4745-L4781","documentation":"exec() looks up the operation name in an internal op-to-thunk map covering find, findOne, findOneAndDelete, findOneAndUpdate, findOneAndReplace, updateOne, updateMany, deleteOne, deleteMany, replaceOne, count, countDocuments, distinct, estimatedDocumentCount and findOneAndRemove. If query.op holds anything else (including a typo or a manually assigned string), Mongoose throws listing the invalid op.","triggerScenarios":"`query.exec('findById')` (not a valid op string); `query.op = 'findone'; query.exec()`; exec('save') or other document-level names; stale plugins setting custom op values.","commonSituations":"Passing a method name that exists on Model but not as a Query op; typos in string ops; copying op names from older Mongoose versions where the list differed.","solutions":["Use a valid op: 'find', 'findOne', 'findOneAndUpdate', 'findOneAndDelete', 'findOneAndReplace', 'updateOne', 'updateMany', 'deleteOne', 'deleteMany', 'replaceOne', 'countDocuments', 'distinct', 'estimatedDocumentCount'.","Prefer not setting op manually — call the chained method (query.find(), query.updateOne(...)) which sets op correctly.","If you passed the op to exec(), verify spelling and casing against the list above."],"exampleFix":"// before\nawait query.exec('findById'); // throws: Query has invalid `op`\n\n// after\nawait Model.findById(id).exec();\n// or: query.find({ _id: id }); await query.exec('find');","handlingStrategy":"type-guard","validationCode":"const VALID_OPS = new Set(['find','findOne','findOneAndUpdate','findOneAndDelete','findOneAndReplace','updateOne','updateMany','deleteOne','deleteMany','replaceOne','count','countDocuments','estimatedDocumentCount','distinct','findOneAndRemove']);\nif (!VALID_OPS.has(op)) throw new Error(`Unknown op '${op}'`);","typeGuard":"const isValidOp = (op) => typeof op === 'string' && VALID_OPS.has(op);","tryCatchPattern":"try { await query.exec(op); } catch (err) { if (err instanceof mongoose.Error && /invalid `op`/.test(err.message)) { /* fix op name and retry */ } throw err; }","preventionTips":["Avoid setting query.op manually; call the chained method instead.","Keep op strings in constants instead of free-form literals.","Note findById is a Model method, not a Query op string."],"tags":["mongoose","query","exec","invalid-op"],"backgroundTag":"mongoose-invalid-query-op","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}