Automattic/mongoose · error · CastError

Cast to Object failed for value "${value}" at path "${path}.

Error message

Cast to Object failed for value "${value}" at path "${path}.${k}"

What it means

Every element of a $or/$and/$nor array must be a condition object. lib/cast.js iterates the array and throws this CastError when an entry is null/undefined or a non-object primitive (string, number, boolean). The path in the message includes the bad index (e.g. '$and.2') so you can locate the entry.

Source

Thrown at lib/cast.js:71

  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);
        }
      }

      // delete empty: {$or: []} -> {}
      if (val.length === 0) {
        delete obj[path];

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Remove null/non-object entries before querying: conds.filter(c => c && typeof c === 'object')
  2. Only push a condition when it exists: if (name) conds.push({ name })
  3. Wrap raw values in a condition object when that is the intent: conds.push({ field: value })

Example fix

// before
const conds = [];
if (name) conds.push({ name });
conds.push(null); // placeholder never removed
Model.find({ $or: conds });

// after
const conds = [];
if (name) conds.push({ name });
Model.find({ $or: conds });
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeLogicalOps(q) {
  for (const key of ['$or', '$and', '$nor']) {
    if (Array.isArray(q[key])) {
      q[key] = q[key].filter(c => c != null && typeof c === 'object');
    }
  }
  return q;
}
await Model.find(sanitizeLogicalOps(query));

Type guard

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

Try / catch

try {
  await Model.find(query);
} catch (err) {
  if (err.name === 'CastError' && err.kind === 'Object' && /^\$(or|and|nor)\./.test(err.path)) {
    // err.path looks like '$and.1' -> drop or fix that element and rebuild the query
  }
  throw err;
}

Prevention

When it happens

Trigger: Model.find({ $and: [null] }); { $or: ['name'] }; { $and: [42] }; condition arrays assembled by pushing raw values (conds.push('age > 5')) or left with holes after delete conds[i].

Common situations: Optional-filter builders that push placeholders and never clean them (arr.push(cond || null)); JSON payloads containing null entries; conditions assembled from user text instead of objects; spreading undefined into arrays.

Related errors


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