Automattic/mongoose · error · MongooseError
Path "${path}" is not an array
Error message
Path "${path}" is not an array What it means
With `applyToArray: true`, a dotted virtual 'a.b' is attached to the array path 'a' itself (callable on the MongooseArray) instead of to each element. That only works when the parent segment resolves to a Mongoose array schematype; otherwise mongoose throws, echoing the resolved parent (which prints 'null' when the parent path does not exist at all).
Source
Thrown at lib/schema.js:2709
if (this.pathType(name) === 'real') {
throw new MongooseError('Virtual path "' + name + '"' +
' conflicts with a real path in the schema');
}
virtuals[name] = parts.reduce(function(mem, part, i) {
mem[part] || (mem[part] = (i === parts.length - 1)
? new VirtualType(options, name)
: {});
return mem[part];
}, this.tree);
if (options?.applyToArray && parts.length > 1) {
const path = this.path(parts.slice(0, -1).join('.'));
if (path?.$isMongooseArray) {
return path.virtual(parts[parts.length - 1], options);
} else {
throw new MongooseError(`Path "${path}" is not an array`);
}
}
return virtuals[name];
};
/**
* Returns the virtual type with the given `name`.
*
* @param {string} name The name of the Virtual to get
* @return {VirtualType|null}
*/
Schema.prototype.virtualpath = function(name) {
return Object.hasOwn(this.virtuals, name) ? this.virtuals[name] : null;
};
/**View on GitHub (pinned to 49cdab0136)
Solutions
- Declare the parent as an array of subdocuments: `items: [new Schema({ ... })]`.
- Drop applyToArray when you want the virtual applied to each element instead of the array itself.
- Check first: `const parent = schema.path(name.split('.').slice(0, -1).join('.'))` and require parent?.$isMongooseArray.
Example fix
// before
const s = new Schema({ item: { first: String } }); // not an array
s.virtual('item.upper', { applyToArray: true });
// after
const s = new Schema({ items: [{ first: String }] });
s.virtual('items.upper', { applyToArray: true }); Defensive patterns
Strategy: validation
Validate before calling
const canApplyToArray = (schema, dottedName) => {
const parent = dottedName.split('.').slice(0, -1).join('.');
return schema.path(parent)?.$isMongooseArray === true;
};
if (!canApplyToArray(schema, 'items.upper')) throw new Error('parent path is not an array');
schema.virtual('items.upper', { applyToArray: true }); Prevention
- Keep applyToArray virtuals in the same module as the array declaration so schema edits stay in sync.
- Write a schema-shape unit test asserting the parents of applyToArray virtuals are arrays.
- Remember a missing parent prints as null in the message - treat that as a name typo.
When it happens
Trigger: `schema.virtual('items.first', { applyToArray: true })` where 'items' is a nested object or single subdocument rather than an array; a parent-segment typo ('item.upper' when the path is 'items') so this.path() returns null.
Common situations: A schema that originally had arrays later changed to a single embedded doc while the virtual kept applyToArray; copy-paste of the array-virtual pattern onto non-array paths.
Related errors
- Cannot set populate virtual as a property of an array
- Reference virtuals require `localField` option
- Reference virtuals require `foreignField` option
- Virtual path "${name}" conflicts with a real path in the sch
- `enum` can only be set on an array of strings or numbers , n
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/6015f41111d66c60.
Report an issue: GitHub.