Automattic/mongoose · error · Error

Got null array filter in ${arrayFilters}

Error message

Got null array filter in ${arrayFilters}

What it means

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.

Source

Thrown at lib/helpers/update/castArrayFilters.js:42

  }
  if (schema._userProvidedOptions.strictQuery != null) {
    strictQuery = schema._userProvidedOptions.strictQuery;
  }
  if (query._mongooseOptions.strictQuery != null) {
    strictQuery = query._mongooseOptions.strictQuery;
  }

  _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilter, query);
};

function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilter, query) {
  // Map to store discriminator values for embedded documents in the array filters.
  // This is used to handle cases where array filters target specific embedded document types.
  const discriminatorValueMap = {};

  for (const filter of arrayFilters) {
    if (filter == null) {
      throw new Error(`Got null array filter in ${arrayFilters}`);
    }
    const keys = Object.keys(filter).filter(key => filter[key] != null);
    if (keys.length === 0) {
      continue;
    }

    const firstKey = keys[0];
    if (firstKey === '$and' || firstKey === '$or') {
      for (const key of keys) {
        _castArrayFilters(filter[key], schema, strictQuery, updatedPathsByFilter, query);
      }
      continue;
    }
    const dot = firstKey.indexOf('.');
    const filterWildcardPath = dot === -1 ? firstKey : firstKey.substring(0, dot);
    if (updatedPathsByFilter[filterWildcardPath] == null) {
      continue;
    }

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Filter out nullish entries: arrayFilters: filters.filter(f => f != null)
  2. Provide exactly one non-null object per identifier used in the update paths
  3. Use [{}] (empty object matches all array elements) when you genuinely need an unconditioned filter, never null

Example fix

// before
const filters = [cond ? { 'elem.a': cond } : null];
await Model.updateOne({}, { $set: { 'items.$[elem].qty': 5 } }, { arrayFilters: filters });

// after
const filters = [{ 'elem.a': cond ?? { $exists: true } }];
await Model.updateOne({}, { $set: { 'items.$[elem].qty': 5 } }, { arrayFilters: filters.filter(f => f != null) });
Defensive patterns

Strategy: validation

Validate before calling

// Validate arrayFilters before executing the update
function validArrayFilters(filters) {
  if (!Array.isArray(filters) || filters.some(f => f == null || typeof f !== 'object')) {
    throw new Error('arrayFilters must be an array of non-null objects');
  }
  return filters;
}

Type guard

const hasValidArrayFilters = (opts) => !opts?.arrayFilters || (Array.isArray(opts.arrayFilters) && opts.arrayFilters.every(f => f != null && typeof f === 'object' && !Array.isArray(f)));

Try / catch

try {
  await Model.updateOne(f, u, { arrayFilters });
} catch (err) {
  if (/null array filter/.test(err.message)) {
    // rebuild arrayFilters with .filter(f => f != null) and retry
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: Building arrayFilters from optional request parameters; conditional spreads leaving undefined slots; copying documentation examples with placeholder entries; API gateways deserializing missing objects as null.

Related errors


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