angular/components · error · Error
SelectionSet: index required when trackByFn is used.
Error message
SelectionSet: index required when trackByFn is used.
What it means
SelectionSet._getTrackedByValue throws when a trackByFn was provided but the SelectableWithIndex passed in has a null/undefined index. With a custom track-by function, identity of each item is computed as trackByFn(index, value), so an index is mandatory. isSelected, select, and deselect all route through this check. Dev-mode only.
Source
Thrown at src/cdk-experimental/selection/selection-set.ts:121
const after = this._getCurrentSelection();
this.changed.next({before, after});
}
private _markSelected(key: T | ReturnType<TrackByFunction<T>>, toSelect: SelectableWithIndex<T>) {
this._selectionMap.set(key, toSelect);
}
private _markDeselected(key: T | ReturnType<TrackByFunction<T>>) {
this._selectionMap.delete(key);
}
private _getTrackedByValue(select: SelectableWithIndex<T>) {
if (!this._trackByFn) {
return select.value;
}
if (select.index == null && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw Error('SelectionSet: index required when trackByFn is used.');
}
return this._trackByFn(select.index!, select.value);
}
private _getCurrentSelection(): SelectableWithIndex<T>[] {
return Array.from(this._selectionMap.values());
}
}
View on GitHub (pinned to 0411926e7d)
Solutions
- Pass the index with every call: select({ value, index }), deselect({ value, index }), isSelected({ value, index }).
- If items have no meaningful index, remove the trackByFn so identity falls back to the value itself.
- Make index non-optional at call sites via types so TypeScript catches omissions.
Example fix
// before
set.select({ value: item.value });
// after
set.select({ value: item.value, index: item.index }); Defensive patterns
Strategy: type-guard
Validate before calling
function hasIndex<T>(s: SelectableWithIndex<T>): boolean {
return !trackByInUse || s.index != null;
}
if (!hasIndex(sel)) throw new Error('index required when trackByFn is used'); Type guard
function hasSelectionIndex<T>(s: SelectableWithIndex<T>): s is SelectableWithIndex<T> & { index: number } {
return s.index != null;
} Try / catch
try {
set.select(sel);
} catch (e) {
if ((e as Error).message.includes('index required')) {
throw new Error(`Callers must supply index for ${String(sel.value)} when trackByFn is set`);
}
throw e;
} Prevention
- Type selectable items with a non-optional index whenever trackByFn is configured.
- Grep for select(/deselect(/isSelected( call sites when adding a trackByFn.
- Prefer value-only tracking when items lack stable indices.
When it happens
Trigger: Calling isSelected(value), select({value}), or deselect({value}) — omitting index — on a SelectionSet configured with a trackByFn. Also calling CdkSelection wrappers that forward {value, index: undefined}.
Common situations: Migrating from value-only tracking to trackByFn without updating every call site; storing Selectable objects built before trackBy was added; template code calling select with only a value binding.
Related errors
- CdkSelection: index required when trackBy is used
- SelectionSet: not multiple selection
- CdkSelectAll: missing CdkSelection in the parent
- Unknown data source
- CdkSelection: multiple selection not enabled
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/8a90f269efb525c9.
Report an issue: GitHub.