Automattic/mongoose · error · MongooseError
Can't use ${conditional}
Error message
Can't use ${conditional} What it means
Thrown by SchemaType.prototype.castForQuery() when a query uses a $conditional operator that has no handler registered on this schematype's $conditionalHandlers. Each path type supports a fixed set of operators; using one outside that set (typo or genuinely unsupported for the type) is rejected.
Source
Thrown at lib/schemaType.js:1813
$type: $type
};
/**
* Cast the given value with the given optional query operator.
*
* @param {string} [$conditional] query operator, like `$eq` or `$in`
* @param {any} val
* @param {Query} context
* @return {any}
* @api private
*/
SchemaType.prototype.castForQuery = function($conditional, val, context) {
let handler;
if ($conditional != null) {
handler = this.$conditionalHandlers[$conditional];
if (!handler) {
throw new MongooseError('Can\'t use ' + $conditional);
}
return handler.call(this, val, context);
}
try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
};
/**
* Set & Get the `checkRequired` function
* Override the function the required validator uses to check whether a value
* passes the `required` check. Override this on the individual SchemaType.View on GitHub (pinned to 49cdab0136)
Solutions
- Fix the operator typo and use standard MongoDB query operators ($gt, $in, $eq, $ne, ...)
- Match the operator to the path type: no $regex on Number/Date/Boolean paths — query by exact value or $in instead
- If the field must be searched by pattern, store it as String in the schema
- For genuinely custom operators, register a handler on the type's $conditionalHandlers before building the query
Example fix
// before (age is a Number path)
Model.find({ age: { $regex: /^18/ } });
// after
Model.find({ age: { $gte: 18, $lt: 19 } }); Defensive patterns
Strategy: validation
Validate before calling
const OPS = new Set(['$eq','$ne','$gt','$gte','$lt','$lte','$in','$nin','$exists','$regex','$options','$all','$size','$elemMatch','$not','$near','$within','$geoWithin','$geoIntersects','$center','$centerSphere','$box','$polygon','$maxDistance','$mod','$type','$bitsAllSet','$comment']);
function validateFilter(filter) {
for (const v of Object.values(filter)) {
if (v && typeof v === 'object' && !Array.isArray(v)) {
for (const op of Object.keys(v)) {
if (op.startsWith('$') && !OPS.has(op)) throw new Error(`Unsupported operator: ${op}`);
}
}
}
} Type guard
function isKnownOperator(op) { return /^\$/.test(op) && KNOWN_OPS_FOR_TYPE.has(op); } Try / catch
try { const docs = await Model.find(filter); } catch (err) { if (/Can't use/.test(err.message)) throw new Error(`Bad operator in filter: ${err.message}`); throw err; } Prevention
- Validate dynamically built filters against an operator allowlist before querying
- Keep operator usage aligned with the declared schema type (no $regex on Number paths)
- Add integration tests per query helper so typos surface in CI
When it happens
Trigger: Model.find({ age: { $regex: /18/ } }) on a Number path ($regex has no handler for numbers); a typo like { $ga: 5 } instead of $gt; using $all or $elemMatch on a plain scalar path; passing an operator name directly via castForQuery.
Common situations: Changing a field from String to Number/ObjectId while old queries still use $regex/$options; dynamically building filters from user input that includes arbitrary operator keys; copy-pasting MongoDB shell queries into Mongoose.
Related errors
- Can't use ${conditional} with String.
- Can't use ${conditional}
- Can't use ${conditional} with UUID.
- Invalid addFields() argument. Must be an object
- Query filter must be an object, got an array ${util.inspect(
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/1fc02e5bc8e8d8c1.
Report an issue: GitHub.