{"record":{"id":"8d2459e7c2b45088","repo":"Automattic/mongoose","slug":"aggregate-prototype-exec-no-longer-accepts-a-cal","errorCode":null,"errorMessage":"Aggregate.prototype.exec() no longer accepts a callback","messagePattern":"Aggregate\\.prototype\\.exec\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"warning","filePath":"lib/aggregate.js","lineNumber":1069,"sourceCode":"  return this._pipeline;\n};\n\n/**\n * Executes the aggregate pipeline on the currently bound Model.\n *\n * #### Example:\n *     const result = await aggregate.exec();\n *\n * @return {Promise}\n * @api public\n */\n\nAggregate.prototype.exec = async function exec() {\n  if (!this._model && !this._connection) {\n    throw new MongooseError('Aggregate not bound to any Model');\n  }\n  if (typeof arguments[0] === 'function') {\n    throw new MongooseError('Aggregate.prototype.exec() no longer accepts a callback');\n  }\n\n  if (this._connection) {\n    if (!this._pipeline.length) {\n      throw new MongooseError('Aggregate has empty pipeline');\n    }\n\n    this._optionsForExec();\n\n    const _this = this;\n    return traceAggregate(async function maybeTracedConnectionAggregate() {\n      const cursor = await _this._connection.client.db().aggregate(_this._pipeline, _this.options);\n      return await cursor.toArray();\n    }, () => ({\n      operation: 'aggregate',\n      database: _this._connection.name,\n      serverAddress: _this._connection.host,\n      serverPort: _this._connection.port,","sourceCodeStart":1051,"sourceCodeEnd":1087,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/aggregate.js#L1051-L1087","documentation":"Subdocuments (items of a document array or single nested docs) share the Document prototype, so subdoc.save() exists — but it does NOT write to MongoDB. It only executes the save middleware chain on the subdoc; persistence happens when the top-level parent document is saved. Mongoose warns on every call to make this explicit, with an opt-out for apps that knowingly use it just to run hooks.","triggerScenarios":"const sub = parent.children.id(id); await sub.save(); expecting the subdoc change to be persisted; looping over doc.items and calling item.save(); calling save() on a single nested subdoc (subdoc = doc.nested) to persist a modification.","commonSituations":"Developers assuming uniform Repository-style save() on any entity; refactors that extracted subdoc handling into functions which save the subdoc directly; bugs where changes are lost because only middleware ran; test suites passing because hooks fired while the DB never changed.","solutions":["Save the top-level parent: mutate the subdoc, then await parent.save() — Mongoose serializes the changed subdoc into the parent's update.","If you only want the middleware side effects: await subdoc.save({ suppressWarning: true }) with a comment stating no DB write occurs.","For bulk subdoc updates, consider update operators on the parent: Model.updateOne({ 'items._id': id }, { $set: { 'items.$.qty': 2 } }) to avoid loading/saving the whole document."],"exampleFix":"// before\nconst sub = parent.children.id(itemId);\nsub.qty = 2;\nawait sub.save(); // does NOT persist\n\n// after\nconst sub = parent.children.id(itemId);\nsub.qty = 2;\nawait parent.save(); // persists the subdoc change","handlingStrategy":"validation","validationCode":"async function saveSubdoc(parent, subdoc) {\n  // persistence always flows through the parent document\n  subdoc.markModified?.(); // ensure tracked if mutated via plain object\n  await parent.save();\n}","typeGuard":"const isSubdocument = (doc) => doc != null && doc.$isSingleNested === true || doc?.ownerDocument?.() != null;","tryCatchPattern":null,"preventionTips":["Treat save() as a root-document-only operation; subdocs persist via parent.save().","Wrap subdoc updates in helpers that always receive the parent, so calling sub.save() directly is impossible.","If you intentionally run only middleware on a subdoc, call save({ suppressWarning: true }) and comment why."],"tags":["mongoose","subdocument","save","nested-documents","api-misuse"],"backgroundTag":"save-on-nested-document","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}