{"record":{"id":"5020e78eb4148e60","repo":"Automattic/mongoose","slug":"got-null-array-filter-in-arrayfilters","errorCode":null,"errorMessage":"Got null array filter in ${arrayFilters}","messagePattern":"Got null array filter in (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/helpers/update/castArrayFilters.js","lineNumber":42,"sourceCode":"  }\n  if (schema._userProvidedOptions.strictQuery != null) {\n    strictQuery = schema._userProvidedOptions.strictQuery;\n  }\n  if (query._mongooseOptions.strictQuery != null) {\n    strictQuery = query._mongooseOptions.strictQuery;\n  }\n\n  _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilter, query);\n};\n\nfunction _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilter, query) {\n  // Map to store discriminator values for embedded documents in the array filters.\n  // This is used to handle cases where array filters target specific embedded document types.\n  const discriminatorValueMap = {};\n\n  for (const filter of arrayFilters) {\n    if (filter == null) {\n      throw new Error(`Got null array filter in ${arrayFilters}`);\n    }\n    const keys = Object.keys(filter).filter(key => filter[key] != null);\n    if (keys.length === 0) {\n      continue;\n    }\n\n    const firstKey = keys[0];\n    if (firstKey === '$and' || firstKey === '$or') {\n      for (const key of keys) {\n        _castArrayFilters(filter[key], schema, strictQuery, updatedPathsByFilter, query);\n      }\n      continue;\n    }\n    const dot = firstKey.indexOf('.');\n    const filterWildcardPath = dot === -1 ? firstKey : firstKey.substring(0, dot);\n    if (updatedPathsByFilter[filterWildcardPath] == null) {\n      continue;\n    }","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/helpers/update/castArrayFilters.js#L24-L60","documentation":"Mongoose validates every entry of the arrayFilters option used with 'a.$[identifier]' update paths. A null or undefined entry in the arrayFilters array is always a client-side bug (usually from building the array dynamically), so Mongoose throws this plain Error before sending the update to MongoDB.","triggerScenarios":"Model.updateOne({}, { $set: { 'items.$[elem].qty': 5 } }, { arrayFilters: [null] }) or arrays assembled with holes/optionals: arrayFilters: [...maybeFilters, undefined], [{ 'elem.a': 1 }, undefined], or .map() callbacks that return undefined for some inputs.","commonSituations":"Building arrayFilters from optional request parameters; conditional spreads leaving undefined slots; copying documentation examples with placeholder entries; API gateways deserializing missing objects as null.","solutions":["Filter out nullish entries: arrayFilters: filters.filter(f => f != null)","Provide exactly one non-null object per identifier used in the update paths","Use [{}] (empty object matches all array elements) when you genuinely need an unconditioned filter, never null"],"exampleFix":"// before\nconst filters = [cond ? { 'elem.a': cond } : null];\nawait Model.updateOne({}, { $set: { 'items.$[elem].qty': 5 } }, { arrayFilters: filters });\n\n// after\nconst filters = [{ 'elem.a': cond ?? { $exists: true } }];\nawait Model.updateOne({}, { $set: { 'items.$[elem].qty': 5 } }, { arrayFilters: filters.filter(f => f != null) });","handlingStrategy":"validation","validationCode":"// Validate arrayFilters before executing the update\nfunction validArrayFilters(filters) {\n  if (!Array.isArray(filters) || filters.some(f => f == null || typeof f !== 'object')) {\n    throw new Error('arrayFilters must be an array of non-null objects');\n  }\n  return filters;\n}","typeGuard":"const hasValidArrayFilters = (opts) => !opts?.arrayFilters || (Array.isArray(opts.arrayFilters) && opts.arrayFilters.every(f => f != null && typeof f === 'object' && !Array.isArray(f)));","tryCatchPattern":"try {\n  await Model.updateOne(f, u, { arrayFilters });\n} catch (err) {\n  if (/null array filter/.test(err.message)) {\n    // rebuild arrayFilters with .filter(f => f != null) and retry\n  } else throw err;\n}","preventionTips":["Filter arrayFilters with .filter(f => f != null) whenever built dynamically","Use [{}] for unconditional element matching instead of null","Add runtime assertions on update options in shared data-access helpers"],"tags":["mongoose","array-filters","update","validation"],"backgroundTag":"array-filter-invalid","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}