{"record":{"id":"9494a4c845424523","repo":"Automattic/mongoose","slug":"query-was-already-executed-str","errorCode":null,"errorMessage":"Query was already executed: ${str}","messagePattern":"Query was already executed: (.+?)","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/query.js","lineNumber":4775,"sourceCode":"  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;\n    try {\n      await _this._hooks.execPre('exec', _this, []);\n    } catch (err) {\n      if (err instanceof Kareem.skipWrappedFunction) {\n        skipWrappedFunction = err;\n      } else {\n        throw err;\n      }\n    }\n\n    let res;\n","sourceCodeStart":4757,"sourceCodeEnd":4793,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/query.js#L4757-L4793","documentation":"Since Mongoose 7 a Query instance tracks how many times it has executed (_execCount) and throws on re-execution. In Mongoose 6 re-running a query logged a warning; now each Query is single-use and you must clone it for a second run. This prevents subtle bugs where hooks, options, or cursor state leak between executions.","triggerScenarios":"`const q = Model.find(); await q.exec(); await q.exec();`; sharing a module-level query constant across requests; loops that reuse one query variable; calling then() twice or awaiting a query after already awaiting it.","commonSituations":"Caching a built query and re-running it per request; retry logic that re-executes the same object; upgrading from Mongoose 5/6 where reuse merely warned.","solutions":["Clone before re-running: `await q.clone().exec()`.","Rebuild the query from a factory function each time instead of storing the Query object.","For retries, wrap the query creation inside the retry loop so each attempt gets a fresh query."],"exampleFix":"// before\nconst q = Model.find({ status: 'active' });\nconst a = await q.exec();\nconst b = await q.exec(); // throws: Query was already executed\n\n// after\nconst base = Model.find({ status: 'active' });\nconst a = await base.clone().exec();\nconst b = await base.clone().exec();","handlingStrategy":"fallback","validationCode":"function runQuery(queryFactory) { return queryFactory().exec(); } // each call builds a new query\n// or: if (q._execCount > 0) q = q.clone();","typeGuard":null,"tryCatchPattern":"try { return await q.exec(); } catch (err) { if (err instanceof mongoose.Error && /already executed/.test(err.message)) { return q.clone().exec(); } throw err; }","preventionTips":["Store query factory functions, not Query instances, for reuse.","Use q.clone() for every re-run.","Create fresh queries inside retry loops."],"tags":["mongoose","query-reuse","exec","migration"],"backgroundTag":"mongoose-query-already-executed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}