{"record":{"id":"2cc0f7b44fff5aa4","repo":"Automattic/mongoose","slug":"cast-to-object-failed-for-value-value-at-path","errorCode":null,"errorMessage":"Cast to Object failed for value \"${value}\" at path \"${path}.${k}\"","messagePattern":"Cast to Object failed for value \"(.+?)\" at path \"(.+?)\\.(.+?)\"","errorType":"exception","errorClass":"CastError","httpStatus":null,"severity":"error","filePath":"lib/cast.js","lineNumber":71,"sourceCode":"  let schematype;\n  let nested;\n  let path;\n  let type;\n  let val;\n\n  options = options || {};\n\n  while (i--) {\n    path = paths[i];\n    val = obj[path];\n\n    if (path === '$or' || path === '$nor' || path === '$and') {\n      if (!Array.isArray(val)) {\n        throw new CastError('Array', val, path);\n      }\n      for (let k = val.length - 1; k >= 0; k--) {\n        if (val[k] == null || typeof val[k] !== 'object') {\n          throw new CastError('Object', val[k], path + '.' + k);\n        }\n        const beforeCastKeysLength = Object.keys(val[k]).length;\n        const discriminatorValue = val[k][schema.options.discriminatorKey];\n        if (discriminatorValue == null) {\n          val[k] = cast(schema, val[k], options, context);\n        } else {\n          const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue);\n          val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context);\n        }\n\n        if (utils.hasOwnKeys(val[k]) === false && beforeCastKeysLength !== 0) {\n          val.splice(k, 1);\n        }\n      }\n\n      // delete empty: {$or: []} -> {}\n      if (val.length === 0) {\n        delete obj[path];","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/cast.js#L53-L89","documentation":"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.","triggerScenarios":"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].","commonSituations":"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.","solutions":["Remove null/non-object entries before querying: conds.filter(c => c && typeof c === 'object')","Only push a condition when it exists: if (name) conds.push({ name })","Wrap raw values in a condition object when that is the intent: conds.push({ field: value })"],"exampleFix":"// before\nconst conds = [];\nif (name) conds.push({ name });\nconds.push(null); // placeholder never removed\nModel.find({ $or: conds });\n\n// after\nconst conds = [];\nif (name) conds.push({ name });\nModel.find({ $or: conds });","handlingStrategy":"validation","validationCode":"function sanitizeLogicalOps(q) {\n  for (const key of ['$or', '$and', '$nor']) {\n    if (Array.isArray(q[key])) {\n      q[key] = q[key].filter(c => c != null && typeof c === 'object');\n    }\n  }\n  return q;\n}\nawait Model.find(sanitizeLogicalOps(query));","typeGuard":"function hasOnlyObjectConditions(q) {\n  return ['$or', '$and', '$nor'].every(k =>\n    !Array.isArray(q[k]) || q[k].every(c => c != null && typeof c === 'object')\n  );\n}","tryCatchPattern":"try {\n  await Model.find(query);\n} catch (err) {\n  if (err.name === 'CastError' && err.kind === 'Object' && /^\\$(or|and|nor)\\./.test(err.path)) {\n    // err.path looks like '$and.1' -> drop or fix that element and rebuild the query\n  }\n  throw err;\n}","preventionTips":["Never push null placeholders into condition arrays","Filter condition arrays through a truthy-object check right before the query","Log the raw condition array when a dynamic query fails so bad entries are visible"],"tags":["mongoose","query","cast","logical-operators","null-safety"],"backgroundTag":"query-cast-failed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}