Automattic/mongoose · error · MongooseError

Cannot manually populate single nested subdoc underneath Map

Error message

Cannot manually populate single nested subdoc underneath Map at path "${this.$__path}". Try using an array instead of a Map.

What it means

Thrown by MongooseMap.$__set() when a Map path is populated and you assign into it, and the Map's values are single nested subdocuments. Mongoose supports in-place populate casting only for arrays of subdocs; a populated single-nested value inside a Map cannot be cast, so it asks you to change the schema shape.

Source

Thrown at lib/types/map.js:116

    // you can't get access to `$__schemaType` to cast in the initial call to
    // `set()` from the `super()` constructor.

    if (this.$__schemaType == null) {
      this.$__deferred = this.$__deferred || [];
      this.$__deferred.push({ key: key, value: value });
      return;
    }

    let _fullPath;
    const parent = this.$__parent;
    const populated = parent?.$__?.populated ?
      parent.$populated(fullPath.call(this), true) || parent.$populated(this.$__path, true) :
      null;
    const priorVal = this.get(key);

    if (populated != null) {
      if (this.$__schemaType.$isSingleNested) {
        throw new MongooseError(
          'Cannot manually populate single nested subdoc underneath Map ' +
          `at path "${this.$__path}". Try using an array instead of a Map.`
        );
      }
      if (Array.isArray(value) && this.$__schemaType.$isMongooseArray) {
        value = value.map(v => {
          if (v.$__ == null) {
            v = new populated.options[populateModelSymbol](v);
          }
          // Doesn't support single nested "in-place" populate
          v.$__.wasPopulated = { value: v._doc._id };
          return v;
        });
      } else if (value != null) {
        if (value.$__ == null) {
          value = new populated.options[populateModelSymbol](value);
        }
        // Doesn't support single nested "in-place" populate

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Change the field to an array of subdocuments: { type: [{ type: SubSchema, ref: 'Model' }] } and populate normally
  2. Keep the Map of plain _ids ({ type: Map, of: ObjectId }) and populate through a virtual or manual lookup
  3. Avoid writing to the Map while it is populated — depopulate or rebuild the document first

Example fix

// before
const s = new Schema({ refs: { type: Map, of: new Schema({}, { _id: false }) } });
// after
const s = new Schema({ refs: [{ type: Schema.Types.ObjectId, ref: 'Other' }] });
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = new Set(['__proto__', 'constructor', 'prototype']);
function mapSet(map, key, value) {
  const k = String(key);
  if (k.startsWith('$') || k.includes('.') || RESERVED.has(k)) throw new Error(`Invalid map key: ${k}`);
  map.set(k, value);
}

Type guard

function isSingleNestedMap(schemaType) {
  return schemaType.$isMongooseMap && schemaType.$__schemaType?.$isSingleNested === true;
}

Try / catch

try { doc.refs.set(key, val); } catch (err) { if (/single nested subdoc underneath Map/.test(err.message)) throw new Error('Schema change required: use an array of subdocs instead of Map for this path'); throw err; }

Prevention

When it happens

Trigger: Schema: { refs: { type: Map, of: new Schema({ ... }) } } (single nested values), followed by await doc.populate('refs') and then doc.refs.set('key', { ... }) on the populated map.

Common situations: Modeling keyed relationships as Map of subdocs instead of arrays, then trying to populate and mutate in place.

Related errors


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