angular/components · warning

Default implementation of filterPredicate requires data to b

Error message

Default implementation of filterPredicate requires data to be a non-null object.

What it means

MatTableDataSource's default filterPredicate expects each row (data) to be a non-null object because it iterates object values with Object.values and stringifies them for matching. In dev mode it warns via console.warn when a row is not an object (e.g. a primitive, null, or undefined). The warning does not stop filtering, but the default predicate cannot match against such rows correctly.

Source

Thrown at src/material/table/table-data-source.ts:240

    });
  };

  /**
   * Checks if a data object matches the data source's filter string. By default, each data object
   * is converted to a string of its properties and returns true if the filter has
   * at least one occurrence in that string. By default, the filter string has its whitespace
   * trimmed and the match is case-insensitive. May be overridden for a custom implementation of
   * filter matching.
   * @param data Data object used to check against the filter.
   * @param filter Filter string that has been set on the data source.
   * @returns Whether the filter matches against the data
   */
  filterPredicate: (data: T, filter: string) => boolean = (data: T, filter: string): boolean => {
    if (
      (typeof ngDevMode === 'undefined' || ngDevMode) &&
      (typeof data !== 'object' || data === null)
    ) {
      console.warn(
        'Default implementation of filterPredicate requires data to be a non-null object.',
      );
    }

    // Transform the filter by converting it to lowercase and removing whitespace.
    const transformedFilter = filter.trim().toLowerCase();
    // Loops over the values in the array and returns true if any of them match the filter string
    // TODO: Remove `as object` cast when `T` stops extending `any`:
    return Object.values(data as object).some(value =>
      `${value}`.toLowerCase().includes(transformedFilter),
    );
  };

  constructor(initialData: T[] = []) {
    super();
    this._data = new BehaviorSubject<T[]>(initialData);
    this._updateChangeSubscription();
  }

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Wrap primitives in objects: data = values.map(v => ({value: v})) and bind that to the data source.
  2. Provide a custom filterPredicate that handles your row type: dataSource.filterPredicate = (data, filter) => data.toString().includes(filter).
  3. Sanitize the data array before assignment (filter out null/undefined rows).

Example fix

// before
datasource.data = ['apple', 'banana'];
// after
datasource.data = ['apple', 'banana'].map(name => ({name}));
// or
datasource.filterPredicate = (data: string, filter: string) => data.includes(filter);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!rows.every(r => r !== null && typeof r === 'object')) {
  throw new Error('MatTableDataSource rows must be non-null objects');
}

Type guard

function isFilterableRow<T>(row: T | null | undefined): row is T & object {
  return row !== null && row !== undefined && typeof row === 'object';
}

Prevention

When it happens

Trigger: Assigning MatTableDataSource a data array whose elements are primitives (string/number) or containing null/undefined rows, then setting the `filter` property so the default filterPredicate runs on each row.

Common situations: Binding a table directly to an array of strings or numbers (e.g. a list of names); API returning arrays of scalars; null entries from unfiltered API responses; developers expecting default filtering to work on primitive rows.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/48d5f0b05bbfa539. Report an issue: GitHub.