angular/components · error
cdkStepper: Cannot assign out-of-bounds value to `selectedIn
Error message
cdkStepper: Cannot assign out-of-bounds value to `selectedIndex`.
What it means
The CdkStepper's selectedIndex setter validates that the assigned index refers to an existing step before applying it. Assigning an index below 0 or >= the number of steps throws, preventing the stepper from entering an invalid state with no selected step.
Source
Thrown at src/cdk/stepper/stepper.ts:362
@Input({transform: booleanAttribute})
get linear(): boolean {
return this._linear();
}
set linear(value: boolean) {
this._linear.set(value);
}
private _linear = signal(false);
/** The index of the selected step. */
@Input({transform: numberAttribute})
get selectedIndex(): number {
return this._selectedIndex();
}
set selectedIndex(index: number) {
if (this._steps) {
// Ensure that the index can't be out of bounds.
if (!this._isValidIndex(index) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw Error('cdkStepper: Cannot assign out-of-bounds value to `selectedIndex`.');
}
if (this.selectedIndex !== index) {
this.selected?._markAsInteracted();
if (
!this._anyControlsInvalidOrPending(index) &&
(index >= this.selectedIndex || this.steps.toArray()[index].editable)
) {
this._updateSelectedItemIndex(index);
}
}
} else {
this._selectedIndex.set(index);
}
}
private _selectedIndex = signal(0);
View on GitHub (pinned to 0411926e7d)
Solutions
- Clamp the index before assigning: stepper.selectedIndex = Math.min(Math.max(0, i), stepper.steps.length - 1)
- Only assign after the steps are initialized (wait for afterContentInit or the steps.changes / _steps signal to be populated)
- Guard against empty step lists: if (stepper.steps.length > 0) { ... }
- Fix the source of the out-of-range value (route params, stored state, computed index) and validate it first
Example fix
// before
this.stepper.selectedIndex = this.savedStep; // may be out of bounds
// after
const count = this.stepper.steps.length;
if (count > 0) {
this.stepper.selectedIndex = Math.min(Math.max(0, this.savedStep), count - 1);
} Defensive patterns
Strategy: validation
Validate before calling
const count = stepper.steps.length;
if (index >= 0 && index < count) {
stepper.selectedIndex = index;
} else {
console.warn(`Ignoring out-of-bounds step index ${index} (steps: ${count})`);
} Type guard
function isValidStepIndex(stepper: CdkStepper, index: number): boolean {
return Number.isInteger(index) && index >= 0 && index < stepper.steps.length;
} Try / catch
try {
stepper.selectedIndex = requestedIndex;
} catch (e) {
if (e instanceof Error && e.message.includes('out-of-bounds')) {
stepper.selectedIndex = 0; // fall back to the first step
} else {
throw e;
}
} Prevention
- Clamp stored/restored step indexes against steps.length before assigning
- Wait until steps are registered (afterContentInit or steps.changes) before setting the index
- Validate route/store-derived indexes before applying them
- Guard against empty step lists from conditional step templates
When it happens
Trigger: Setting stepper.selectedIndex = n where n < 0 or n >= steps.length; assigning before steps are registered (steps query list still empty); computing an index from data that can be empty; using step.interacted/next/previous logic that derives a bad index.
Common situations: Restoring a selected step from a URL/route param that no longer exists; a stepper whose steps are rendered conditionally so the index is out of range at assignment time; binding [selectedIndex] to a computed value that starts at -1 before data loads.
Related errors
- ListKeyManager constructed with a signal must receive an inj
- KeyManager items in typeahead mode must implement the `getLa
- CdkDropList could not find an element container matching the
- Invalid DOM structure for drop list. Alternate container ele
- Invalid DOM structure for drop list. All items must be place
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/5a133b7b8a6b7e5b.
Report an issue: GitHub.