Automattic/mongoose · error · MongooseError
Provided object has both field "${name}" and its alias "${al
Error message
Provided object has both field "${name}" and its alias "${alias}" What it means
Model.translateAliases(fields, errorOnDuplicates) rewrites aliased keys to their real schema paths. When errorOnDuplicates is true — which the query option `translateAliases: true` uses — it throws if the object contains BOTH a field and its alias, because after translation both keys would target the same path and one value would silently clobber the other. The throw fires while walking each dot-separated segment of a key against schema aliases.
Source
Thrown at lib/model.js:1958
*
* @param {object} fields fields/conditions that may contain aliased keys
* @param {boolean} [errorOnDuplicates] if true, throw an error if there's both a key and an alias for that key in `fields`
* @return {object} the translated 'pure' fields/conditions
*/
Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
_checkContext(this, 'translateAliases');
const translate = (key, value) => {
let alias;
const translated = [];
const fieldKeys = key.split('.');
let currentSchema = this.schema;
for (const i in fieldKeys) {
const name = fieldKeys[i];
if (currentSchema?.aliases[name]) {
alias = currentSchema.aliases[name];
if (errorOnDuplicates && alias in fields) {
throw new MongooseError(`Provided object has both field "${name}" and its alias "${alias}"`);
}
// Alias found,
translated.push(alias);
} else {
alias = name;
// Alias not found, so treat as un-aliased key
translated.push(name);
}
// Check if aliased path is a schema
if (currentSchema?.paths[alias]) {
currentSchema = currentSchema.paths[alias].schema;
}
else
currentSchema = null;
}
const translatedKey = translated.join('.');View on GitHub (pinned to 49cdab0136)
Solutions
- Remove one of the conflicting keys — standardize the codebase on either aliases or real field names for that object
- Sanitize input before the query: drop alias keys when their target field is also present (decide precedence explicitly)
- Keep `translateAliases` unset/false where mixed input is expected and map keys manually
- If both values are legitimate, rename one to a distinct schema path instead of aliasing
Example fix
// schema: new Schema({ years: { type: Number, alias: 'age' } })
// before
User.find({ years: 5, age: 5 }, null, { translateAliases: true });
// after — keep only the alias (or only the field)
User.find({ age: 5 }, null, { translateAliases: true }); Defensive patterns
Strategy: validation
Validate before calling
// Reject/drop alias-vs-field duplicates before enabling translateAliases
function hasAliasConflict(schema, fields) {
return Object.keys(fields ?? {}).some(key => {
const alias = schema.aliases[key];
return alias != null && alias in fields;
});
}
if (hasAliasConflict(User.schema, filter)) {
filter = pickOneOfAliasPair(User.schema, filter); // your explicit precedence rule
}
const docs = await User.find(filter, null, { translateAliases: true }); Try / catch
try {
await User.find(filter, null, { translateAliases: true });
} catch (err) {
if (err instanceof mongoose.Error && /Provided object has both field/.test(err.message)) {
// strip the duplicate key per your precedence rule and retry once
}
} Prevention
- Standardize each object producer (form, API client, seeder) on either aliases or raw field names — never mix
- When merging objects that may contain aliases, dedupe with a schema.aliases-aware merge helper before querying
- Add a unit test that feeds every public filter-bearing endpoint an alias+field pair once
When it happens
Trigger: Schema declares `years: { type: Number, alias: 'age' }`, then `Model.find({ years: 5, age: 5 }, null, { translateAliases: true })`; same conflict in update/projection objects for findOneAndUpdate with translateAliases; direct call `Model.translateAliases({ years: 5, age: 5 }, true)`; nested paths where a subdocument schema aliases a field that also appears under its real name.
Common situations: API request bodies merged from multiple sources (query params + defaults) that carry both the alias and the field name; frontend forms switching to the alias while backend defaults still set the raw field; toggling translateAliases on after alias adoption was only partial.
Related errors
- Aggregate.prototype.explain() no longer accepts a callback
- Aggregate has empty pipeline
- Aggregate pipeline for $unionWith cannot include `$out` or `
- Invalid value for alias option on ${prop}, got ${a}
- Invalid value for alias option on ${prop}, got ${alias}
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/94e9acb0ad9f9f94.
Report an issue: GitHub.