angular/components · warning
trackBy must be a function, but received ${JSON.stringify(fn
Error message
trackBy must be a function, but received ${JSON.stringify(fn)}. What it means
The CDK data-table's `trackBy` input must be a `TrackByFunction<T>`. In dev mode, assigning a non-function truthy value logs this warning (the value is stored anyway, and Angular will then break at runtime when it tries to call it). The library warns instead of throwing so that production builds with `ngDevMode` stripped are unaffected.
Source
Thrown at src/cdk/table/table.ts:500
}
return this._cellRoleInternal;
}
private _cellRoleInternal: string | null | undefined = undefined;
/**
* Tracking function that will be used to check the differences in data changes. Used similarly
* to `ngFor` `trackBy` function. Optimize row operations by identifying a row based on its data
* relative to the function to know if a row should be added/removed/moved.
* Accepts a function that takes two parameters, `index` and `item`.
*/
@Input()
get trackBy(): TrackByFunction<T> {
return this._trackByFn;
}
set trackBy(fn: TrackByFunction<T>) {
if ((typeof ngDevMode === 'undefined' || ngDevMode) && fn != null && typeof fn !== 'function') {
console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}.`);
}
this._trackByFn = fn;
}
private _trackByFn!: TrackByFunction<T>;
/**
* The table's source of data, which can be provided in three ways (in order of complexity):
* - Simple data array (each object represents one table row)
* - Stream that emits a data array each time the array changes
* - `DataSource` object that implements the connect/disconnect interface.
*
* If a data array is provided, the table must be notified when the array's objects are
* added, removed, or moved. This can be done by calling the `renderRows()` function which will
* render the diff since the last table render. If the data array reference is changed, the table
* will automatically trigger an update to the rows.
*
* When providing an Observable stream, the table will trigger an update automatically when the
* stream emits a new array of data.View on GitHub (pinned to 0411926e7d)
Solutions
- Bind an actual function reference: `trackBy` should receive `(index, item) => ...` or a method reference.
- Remove any `()` call or quotes in the template binding so the function itself is passed.
- Verify the assigned value with `typeof` before setting it if it comes from dynamic code.
Example fix
<!-- before -->
<table cdk-table [dataSource]="data" [trackBy]="'trackById'"></table>
<!-- after -->
<table cdk-table [dataSource]="data" [trackBy]="trackById"></table>
// component: trackById(index: number, row: Row) { return row.id; } Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof trackById !== 'function') {
throw new TypeError('trackBy must be a function');
}
table.trackBy = trackById; Type guard
function isTrackByFn<T>(v: unknown): v is TrackByFunction<T> {
return typeof v === 'function';
} Prevention
- Never quote or invoke trackBy in templates; pass the function reference: [trackBy]="trackById".
- Add a compile-time type like `trackBy: TrackByFunction<Row>` in reactive configs.
- Unit-test that the bound value is a function in component tests.
When it happens
Trigger: Binding something other than a function to `trackBy`, e.g. `trackBy="myTrackById"` in a template (string, not function reference), `trackBy={id: ...}` object, or calling it: `trackBy="trackById()"`.
Common situations: Copy-pasting `trackBy` usage from `*ngFor` examples where a string worked differently, forgetting the `this.`/method reference, or binding an observable-mapped value that isn't a function.
Related errors
- CdkSelectionColumn: missing CdkSelection in the parent
- CdkSelectionColumn: missing parent table
- SelectionSet: index required when trackByFn is used.
- CdkSelection: index required when trackBy is used
- ListKeyManager constructed with a signal must receive an inj
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/1f7dc48185f7b784.
Report an issue: GitHub.