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

  1. Clamp the index before assigning: stepper.selectedIndex = Math.min(Math.max(0, i), stepper.steps.length - 1)
  2. Only assign after the steps are initialized (wait for afterContentInit or the steps.changes / _steps signal to be populated)
  3. Guard against empty step lists: if (stepper.steps.length > 0) { ... }
  4. 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

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


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