Automattic/mongoose · error · MongooseError

Expected path "${path}" to be populated

Error message

Expected path "${path}" to be populated

What it means

Document.prototype.$assertPopulated(path, values) is a fail-fast guard: it optionally $set()s the second argument, then throws MongooseError if $populated(path) is falsy, i.e. the path is not currently populated. It exists so code that depends on populated data crashes immediately with a clear path name instead of silently reading raw ObjectId refs.

Source

Thrown at lib/document.js:5064

 * @return {Document} this
 * @memberOf Document
 * @method $assertPopulated
 * @instance
 * @api public
 */

Document.prototype.$assertPopulated = function $assertPopulated(path, values) {
  if (Array.isArray(path)) {
    path.forEach(p => this.$assertPopulated(p, values));
    return this;
  }

  if (arguments.length > 1) {
    this.$set(values);
  }

  if (!this.$populated(path)) {
    throw new MongooseError(`Expected path "${path}" to be populated`);
  }

  return this;
};

/**
 * Takes a populated field and returns it to its unpopulated state.
 *
 * #### Example:
 *
 *     Model.findOne().populate('author').exec(function (err, doc) {
 *       console.log(doc.author.name); // Dr.Seuss
 *       console.log(doc.depopulate('author'));
 *       console.log(doc.author); // '5144cf8050f071d979c118a7'
 *     })
 *
 * If the path was not provided, then all populated fields are returned to their unpopulated state.
 *

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Populate before asserting: await doc.populate('author') then doc.$assertPopulated('author').
  2. Branch instead of asserting when the path may legitimately be unpopulated: if (doc.$populated('author')) { ... }.
  3. Fix path typos: the path must match the schema path exactly, including full nested paths like 'author.address'.
  4. If you depopulated earlier, re-populate before asserting again.

Example fix

// before
const doc = await Model.findOne({ _id: id });
doc.$assertPopulated('author'); // throws: query had no .populate()

// after
const doc = await Model.findOne({ _id: id }).populate('author');
doc.$assertPopulated('author'); // ok
// or lazily:
if (!doc.$populated('author')) await doc.populate('author');
Defensive patterns

Strategy: validation

Validate before calling

async function ensurePopulated(doc, path) {
  if (!doc.$populated(path)) {
    await doc.populate(path);
  }
  return doc;
}
// usage
await ensurePopulated(doc, 'author');
doc.$assertPopulated('author');

Type guard

function isPopulated(doc, path) {
  return doc.$populated(path) != null;
}

Try / catch

try {
  doc.$assertPopulated('author');
} catch (err) {
  if (err instanceof mongoose.MongooseError && err.message.startsWith('Expected path')) {
    await doc.populate('author'); // populate, then continue
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling doc.$assertPopulated('author') after a findOne() that lacked .populate('author'); after doc.$depopulate('author'); after save/depopulate reset the populated state; or with a typo'd path string that was never populated.

Common situations: Serializer or view code that assumes a path was populated upstream; a refactor dropped a .populate() call from the query; mixing populate and depopulate flows; nested path names not matching the schema exactly.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/48645d6489631f3f. Report an issue: GitHub.