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

  1. Fix the operator typo and use standard MongoDB query operators ($gt, $in, $eq, $ne, ...)
  2. Match the operator to the path type: no $regex on Number/Date/Boolean paths — query by exact value or $in instead
  3. If the field must be searched by pattern, store it as String in the schema
  4. 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

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


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/1fc02e5bc8e8d8c1. Report an issue: GitHub.