Automattic/mongoose · error · MongooseError
Virtual path "${name}" conflicts with a real path in the sch
Error message
Virtual path "${name}" conflicts with a real path in the schema What it means
Before registering a virtual, mongoose checks pathType(name); a result of 'real' means a stored path already occupies that exact name. A virtual cannot shadow a persisted field (its getters/setters would fight the real path), so the definition is refused.
Source
Thrown at lib/schema.js:2693
this.paths[cur].schema.virtual(remnant, options);
break;
} else if (this.paths[cur].$isSchemaMap) {
const remnant = parts.slice(i + 2).join('.');
this.paths[cur].$__schemaType.schema.virtual(remnant, options);
break;
}
cur += '.' + parts[i + 1];
}
return virtual;
}
const virtuals = this.virtuals;
const parts = name.split('.');
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`);
}
}View on GitHub (pinned to 49cdab0136)
Solutions
- Rename the virtual to a free name (e.g. 'displayName', 'nameVirtual').
- Remove or rename the stored field if the virtual replaces it.
- When names are dynamic, guard with `if (schema.pathType(name) !== 'real') schema.virtual(name)`.
Example fix
// before
const s = new Schema({ name: String });
s.virtual('name'); // conflicts with the stored path
// after
const s = new Schema({ name: String });
s.virtual('displayName').get(function() { return this.name?.toUpperCase(); }); Defensive patterns
Strategy: validation
Validate before calling
const safeVirtual = (schema, name, opts) => {
if (schema.pathType(name) === 'real') {
throw new Error(`path ${name} already exists as a real path; pick another virtual name`);
}
return schema.virtual(name, opts);
}; Prevention
- Namespace plugin virtuals (e.g. 'myPluginComputed') to avoid collisions with stored fields.
- Grep the schema definition for a field name before adding a virtual with the same name.
- When names are user-driven, check schema.pathType(name) first.
When it happens
Trigger: `new Schema({ name: String })` followed by `schema.virtual('name')`; `schema.virtual('a.b')` when 'a.b' is a real nested path; plugins registering virtuals over existing fields on schemas they do not control.
Common situations: Adding a computed getter that collides with a stored field (e.g. a stored 'fullName' plus a computed one); plugin/schema composition across teams; renaming fields but keeping old virtuals under the old names.
Related errors
- Reference virtuals require `localField` option
- Reference virtuals require `foreignField` option
- Path "${path}" is not an array
- Cannot set populate virtual as a property of an array
- Attempting to remove virtual "${virtual}" that does not exis
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/34e6bc4bef6d7373.
Report an issue: GitHub.