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" populateView on GitHub (pinned to 49cdab0136)
Solutions
- Change the field to an array of subdocuments: { type: [{ type: SubSchema, ref: 'Model' }] } and populate normally
- Keep the Map of plain _ids ({ type: Map, of: ObjectId }) and populate through a virtual or manual lookup
- 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
- Do not combine populate with Maps of single nested subdocs — model them as arrays
- Store plain ObjectIds in Maps and resolve relations via separate queries or virtuals
- Document the limitation in schema review checklists for populated paths
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
- Mongoose does not support calling populate() on nested docs.
- Invalid addFields() argument. Must be an object
- `refPath` must be a string or a function that returns a stri
- Document.prototype.populate() no longer accepts a callback
- Expected path "${path}" to be populated
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/b900a2220f846f6d.
Report an issue: GitHub.