{"record":{"id":"33085ad44f680922","repo":"Automattic/mongoose","slug":"invalid-addfields-argument-must-be-an-object","errorCode":null,"errorMessage":"Invalid addFields() argument. Must be an object","messagePattern":"Invalid addFields\\(\\) argument\\. Must be an object","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/aggregate.js","lineNumber":208,"sourceCode":" *     aggregate.addFields({\n *         newField: '$b.nested'\n *       , plusTen: { $add: ['$val', 10]}\n *       , sub: {\n *            name: '$a'\n *         }\n *     })\n *\n *     // etc\n *     aggregate.addFields({ salary_k: { $divide: [ \"$salary\", 1000 ] } });\n *\n * @param {object} arg field specification\n * @see $addFields https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/\n * @return {Aggregate}\n * @api public\n */\nAggregate.prototype.addFields = function(arg) {\n  if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) {\n    throw new MongooseError('Invalid addFields() argument. Must be an object');\n  }\n  return this.append({ $addFields: Object.assign({}, arg) });\n};\n\n/**\n * Appends a new $project operator to this aggregate pipeline.\n *\n * Mongoose query [selection syntax](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) is also supported.\n *\n * #### Example:\n *\n *     // include a, include b, exclude _id\n *     aggregate.project(\"a b -_id\");\n *\n *     // or you may use object notation, useful when\n *     // you have keys already prefixed with a \"-\"\n *     aggregate.project({a: 1, b: 1, _id: 0});\n *","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/aggregate.js#L190-L226","documentation":"This TypeError comes from utils.populate(), the normalizer that every Query.populate(), Document.populate(), and object-based populate definition passes through. After flattening the arguments into an options object, it requires obj.path to be either a string or an array of strings; anything else (number, object, null inside an array, symbol) throws synchronously. The message interpolates the typeof of the original path argument so you can see what type you actually passed.","triggerScenarios":"Calling .populate(42), .populate({ path: { nested: true } }), .populate(['user', 123]), or .populate({ path: ['a', null] }); passing a variable that was expected to be a path string but is an options object, e.g. .populate({ match: { x: 1 } }) with no path; template-built paths where the variable is undefined (typeof \"undefined\").","commonSituations":"Mixing up the two populate signatures (string path vs object spec); passing an array of populate objects where a string element is required; refactoring populate configs into variables and losing the path key; receiving populate specs from an API/JSON payload where a path is missing or numeric.","solutions":["Pass the path as a string: .populate('author') or as an object with a string path: .populate({ path: 'author', select: 'name' }).","If passing an array, ensure every element is a string path or a valid populate object: .populate([{ path: 'author' }, { path: 'comments' }]).","Check the variable you are interpolating — a typeof \"undefined\" in the message means the path variable was never set.","Validate externally supplied populate specs before handing them to Mongoose."],"exampleFix":"// before\nconst path = req.query.expand; // may be undefined or an object\nconst docs = await Model.find().populate(path);\n\n// after\nconst expand = req.query.expand;\nconst pop = typeof expand === 'string' ? expand : Array.isArray(expand) && expand.every(p => typeof p === 'string') ? expand : [];\nconst docs = await Model.find().populate(pop);","handlingStrategy":"type-guard","validationCode":"function normalizePopulate(path) {\n  if (typeof path === 'string') return path;\n  if (Array.isArray(path) && path.every(p => typeof p === 'string')) return path;\n  if (Array.isArray(path) && path.every(p => p && typeof p === 'object' && typeof p.path === 'string')) return path;\n  if (path && typeof path === 'object' && typeof path.path === 'string') return path;\n  return undefined; // skip populate instead of throwing\n}","typeGuard":"const isPopulatePath = (p) =>\n  typeof p === 'string' ||\n  (Array.isArray(p) && p.every(el => typeof el === 'string'));","tryCatchPattern":null,"preventionTips":["Treat externally supplied populate specs as untrusted input; validate before passing them to .populate().","Keep populate configs in typed constants (TS: string | string[] | PopulateOptions[]) instead of inline any.","Remember the object form MUST include a string `path` property."],"tags":["mongoose","populate","typeerror","invalid-argument","query"],"backgroundTag":"invalid-populate-path","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}