Automattic/mongoose · error · MongooseError
${key} is not allowed with sanitizeFilter
Error message
${key} is not allowed with sanitizeFilter What it means
Mongoose's query sanitizer (mongoose.sanitizeFilter() or the sanitizeFilter: true option) wraps operator-bearing values in $eq to block query injection from untrusted objects. It hard-rejects $where and $expr (they allow JavaScript/expression evaluation) and $jsonSchema/$text (top-level operators that cannot be safely wrapped), throwing a MongooseError when any of them appears in a filter that is being sanitized.
Source
Thrown at lib/helpers/query/sanitizeFilter.js:28
}
if (Array.isArray(filter)) {
for (const subfilter of filter) {
sanitizeFilter(subfilter);
}
return filter;
}
const filterKeys = Object.keys(filter);
for (const key of filterKeys) {
const value = filter[key];
if (value?.[trustedSymbol]) {
continue;
}
if (key === '$and' || key === '$or' || key === '$nor') {
sanitizeFilter(value);
continue;
} else if (key === '$jsonSchema' || key === '$where' || key === '$expr' || key === '$text') {
throw new MongooseError(key + ' is not allowed with sanitizeFilter');
}
if (hasDollarKeys(value)) {
const keys = Object.keys(value);
if (keys.length === 1 && keys[0] === '$eq') {
continue;
}
filter[key] = { $eq: filter[key] };
}
}
return filter;
};
View on GitHub (pinned to 49cdab0136)
Solutions
- Mark server-built filters as trusted: Model.find({ $expr: mongoose.trusted({ $gt: ['$a', '$b'] }) })
- Sanitize only the user-supplied fragments, then merge them with trusted operator objects after sanitization
- Rewrite the query without the rejected operator (replace $where with plain field conditions)
- As a last resort, disable the sanitizer for that one query: { sanitizeFilter: false }
Example fix
// before
mongoose.set('sanitizeFilter', true);
await Model.find({ $expr: { $gt: ['$endDate', '$startDate'] } }); // throws
// after
await Model.find({ $expr: mongoose.trusted({ $gt: ['$endDate', '$startDate'] }) }); Defensive patterns
Strategy: validation
Validate before calling
const SANITIZE_FORBIDDEN = new Set(['$where', '$expr', '$jsonSchema', '$text']);
function assertSanitizable(filter) {
for (const k of Object.keys(filter)) {
if (SANITIZE_FORBIDDEN.has(k)) throw new Error(`${k} cannot be used with sanitizeFilter; wrap with mongoose.trusted()`);
if (k === '$and' || k === '$or' || k === '$nor') filter[k].forEach(assertSanitizable);
}
} Type guard
const needsTrusted = (filter) => Object.keys(filter).some(k => ['$where', '$expr', '$jsonSchema', '$text'].includes(k));
Try / catch
try {
await Model.find(mongoose.sanitizeFilter(filter));
} catch (err) {
if (err instanceof mongoose.MongooseError && /not allowed with sanitizeFilter/.test(err.message)) {
// split user input from server-side $expr/$where and re-run with mongoose.trusted()
} else throw err;
} Prevention
- Apply sanitizeFilter only to user-supplied filter fragments, never to server-composed operator queries
- Mark every server-built operator filter with mongoose.trusted() at construction time
- Never build $where from user input even without sanitizeFilter — it is a code-execution vector
When it happens
Trigger: Model.find({ $where: 'this.name === "x"' }) or Model.find({ $expr: { $gt: ['$a', '$b'] } }) while sanitizeFilter is enabled via mongoose.set('sanitizeFilter', true), the sanitizeFilter query option, or by passing the filter through mongoose.sanitizeFilter().
Common situations: Apps that enable sanitizeFilter globally for security and later add a $expr/$text/$where query (search endpoints are the usual offender); middleware that sanitizes every incoming filter; merging trusted server-built operator filters with user filters under one sanitized call.
Related errors
- Must provide `autoEncryption` when connecting with encrypted
- Mongoose maps do not support reserved key name "${key}"
- Arguments must be aggregate pipeline operators
- Invalid addFields() argument. Must be an object
- Invalid project() argument. Must be string or object
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/37607930842f6116.
Report an issue: GitHub.