{"record":{"id":"0643b713161d94a6","repo":"angular/components","slug":"cdkselection-index-required-when-trackby-is-used","errorCode":null,"errorMessage":"CdkSelection: index required when trackBy is used","messagePattern":"CdkSelection: index required when trackBy is used","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cdk-experimental/selection/selection.ts","lineNumber":143,"sourceCode":"  ngAfterContentChecked() {\n    if (this._dataSource && !this._renderChangeSubscription) {\n      this._observeRenderChanges();\n    }\n  }\n\n  ngOnDestroy() {\n    this._destroyed.next();\n    this._destroyed.complete();\n\n    if (isDataSource(this._dataSource)) {\n      this._dataSource.disconnect(this);\n    }\n  }\n\n  /** Toggles selection for a given value. `index` is required if `trackBy` is used. */\n  toggleSelection(value: T, index?: number) {\n    if (!!this.trackByFn && index == null && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n      throw Error('CdkSelection: index required when trackBy is used');\n    }\n\n    if (this.isSelected(value, index)) {\n      this._selection.deselect({value, index});\n    } else {\n      this._selection.select({value, index});\n    }\n  }\n\n  /**\n   * Toggles select-all. If no value is selected, select all values. If all values or some of the\n   * values are selected, de-select all values.\n   */\n  toggleSelectAll() {\n    if (!this._multiple && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n      throw Error('CdkSelection: multiple selection not enabled');\n    }\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk-experimental/selection/selection.ts#L125-L161","documentation":"CdkSelection.toggleSelection throws in dev mode when a trackByFn is configured on the CdkSelection but the caller omits the index argument. With track-by, items are identified by trackByFn(index, value), so toggling identity cannot be computed without the index.","triggerScenarios":"Calling selection.toggleSelection(value) with no second argument on a CdkSelection that has [trackBy] set. Calling without trackBy is fine.","commonSituations":"Click handlers bound only to the row value; template method calls like toggleSelection(row) written before trackBy was added; helper functions that drop optional parameters.","solutions":["Pass the row index: toggleSelection(value, i), e.g., inside *cdkRow or *cdkItem where the index is provided.","Remove the trackBy input if value-only identity is acceptable.","Make index required in wrapper helpers so the compiler enforces it."],"exampleFix":"// before\nselection.toggleSelection(row.value);\n\n// after\nselection.toggleSelection(row.value, row.index);","handlingStrategy":"validation","validationCode":"function canToggle(trackByFn: unknown, index?: number): boolean {\n  return !trackByFn || index != null;\n}\nif (!canToggle(selection.trackByFn, idx)) throw new Error('toggleSelection requires index when trackBy is set');\nselection.toggleSelection(value, idx);","typeGuard":"function hasToggleIndex(value: unknown, index: number | undefined): index is number {\n  return index != null;\n}","tryCatchPattern":"try {\n  selection.toggleSelection(value, index);\n} catch (e) {\n  if ((e as Error).message.includes('index required')) {\n    console.warn('Supply the row index when using trackBy');\n  } else throw e;\n}","preventionTips":["Call toggleSelection from row templates that expose the index (*cdkRow=\"let i = index\").","Make wrapper helpers require the index parameter when trackBy is present.","Remove [trackBy] if toggling by value alone is the intended behavior."],"tags":["angular","cdk","selection","trackby","dev-mode-assertion"],"backgroundTag":"trackby-index-required","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}