Automattic/mongoose · error · CastError

Cast to Array failed for value "${value}" at path "${path}"

Error message

Cast to Array failed for value "${value}" at path "${path}"

What it means

Mongoose throws this CastError while casting query conditions when a logical operator ($or, $nor, or $and) is not an Array. lib/cast.js walks every key of the conditions object and requires these operators to hold a list of sub-condition objects; any other shape fails before the query reaches MongoDB. The error carries the offending value and path (e.g. '$or') for diagnosis.

Source

Thrown at lib/cast.js:67

  const paths = Object.keys(obj);
  let i = paths.length;
  let _keys;
  let any$conditionals;
  let schematype;
  let nested;
  let path;
  let type;
  let val;

  options = options || {};

  while (i--) {
    path = paths[i];
    val = obj[path];

    if (path === '$or' || path === '$nor' || path === '$and') {
      if (!Array.isArray(val)) {
        throw new CastError('Array', val, path);
      }
      for (let k = val.length - 1; k >= 0; k--) {
        if (val[k] == null || typeof val[k] !== 'object') {
          throw new CastError('Object', val[k], path + '.' + k);
        }
        const beforeCastKeysLength = Object.keys(val[k]).length;
        const discriminatorValue = val[k][schema.options.discriminatorKey];
        if (discriminatorValue == null) {
          val[k] = cast(schema, val[k], options, context);
        } else {
          const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue);
          val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context);
        }

        if (utils.hasOwnKeys(val[k]) === false && beforeCastKeysLength !== 0) {
          val.splice(k, 1);
        }
      }

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Pass an array of condition objects: Model.find({ $or: [{ name: 'foo' }, { age: { $gte: 21 } }] })
  2. In dynamic builders, always push whole condition objects: conds.push({ field: value })
  3. Validate incoming filter payloads so $or/$and/$nor are arrays of objects before they reach mongoose

Example fix

// before
Model.find({ $or: { name: 'foo', age: { $gte: 21 } } });

// after
Model.find({ $or: [{ name: 'foo' }, { age: { $gte: 21 } }] });
Defensive patterns

Strategy: validation

Validate before calling

function assertLogicalOpsAreArrays(q) {
  for (const key of ['$or', '$and', '$nor']) {
    if (q[key] === undefined) continue;
    if (!Array.isArray(q[key])) {
      throw new TypeError(`${key} must be an array of condition objects`);
    }
    q[key].forEach((c, i) => {
      if (c == null || typeof c !== 'object') {
        throw new TypeError(`${key}[${i}] must be an object`);
      }
    });
  }
  return q;
}

await Model.find(assertLogicalOpsAreArrays(req.query));

Type guard

function isWellFormedLogicalQuery(q) {
  return ['$or', '$and', '$nor'].every(k =>
    q[k] === undefined ||
    (Array.isArray(q[k]) && q[k].every(c => c != null && typeof c === 'object'))
  );
}

Try / catch

try {
  const docs = await Model.find(query);
} catch (err) {
  if (err.name === 'CastError' && err.kind === 'Array') {
    // err.path names the operator ('$or'/'$and'/'$nor'); rebuild it as [{...}, {...}]
  }
  throw err;
}

Prevention

When it happens

Trigger: Model.find({ $or: { a: 1 } }) -- a single condition object instead of an array of them; { $and: 'x' }; { $nor: { a: 1, b: 2 } }; filters built from JSON request bodies or querystrings where $or arrives as an object.

Common situations: Dynamic query builders that assign instead of push; clients serializing $or as an object; refactors that drop the array brackets; comma-separated strings that were never split into condition objects.

Related errors


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