angular/components · error · Error

FlexibleConnectedPositionStrategy: At least one position is

Error message

FlexibleConnectedPositionStrategy: At least one position is required.

What it means

FlexibleConnectedPositionStrategy requires at least one entry in its preferred positions array; `_validatePositions` throws during strategy validation when `this._preferredPositions` is empty. This is a dev-mode sanity check because a position strategy with no positions cannot compute any placement for the overlay.

Source

Thrown at src/cdk/overlay/position/flexible-connected-position-strategy.ts:1202

    return !this._hasFlexibleDimensions || this._isPushed;
  }

  /** Retrieves the offset of a position along the x or y axis. */
  private _getOffset(position: ConnectedPosition, axis: 'x' | 'y') {
    if (axis === 'x') {
      // We don't do something like `position['offset' + axis]` in
      // order to avoid breaking minifiers that rename properties.
      return position.offsetX == null ? this._offsetX : position.offsetX;
    }

    return position.offsetY == null ? this._offsetY : position.offsetY;
  }

  /** Validates that the current position match the expected values. */
  private _validatePositions(): void {
    if (typeof ngDevMode === 'undefined' || ngDevMode) {
      if (!this._preferredPositions.length) {
        throw Error('FlexibleConnectedPositionStrategy: At least one position is required.');
      }

      // TODO(crisbeto): remove these once Angular's template type
      // checking is advanced enough to catch these cases.
      this._preferredPositions.forEach(pair => {
        validateHorizontalPosition('originX', pair.originX);
        validateVerticalPosition('originY', pair.originY);
        validateHorizontalPosition('overlayX', pair.overlayX);
        validateVerticalPosition('overlayY', pair.overlayY);
      });
    }
  }

  /** Adds a single CSS class or an array of classes on the overlay panel. */
  private _addPanelClasses(cssClasses: string | string[]) {
    if (this._pane) {
      coerceArray(cssClasses).forEach(cssClass => {
        if (cssClass !== '' && this._appliedPanelClasses.indexOf(cssClass) === -1) {

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Pass at least one position object to `withPositions`, e.g. `strategy.withPositions([{originX:'start',originY:'bottom',overlayX:'start',overlayY:'top'}])`
  2. Use `withDefaultPosition(defaultPos)` before `withPositions` so there is always a fallback
  3. Guard dynamic position arrays: fall back to a default when the computed array is empty
  4. Check that the config object feeding positions is not undefined or an empty object

Example fix

// before
const strategy = overlay.position().flexibleConnectedTo(origin).withPositions(myPositions);
// after
const positions = myPositions?.length ? myPositions : [{originX:'start', originY:'bottom', overlayX:'start', overlayY:'top'}];
const strategy = overlay.position().flexibleConnectedTo(origin).withPositions(positions);
Defensive patterns

Strategy: validation

Validate before calling

function hasPositions(positions) { return Array.isArray(positions) && positions.length > 0; }
const strategy = overlay.position().flexibleConnectedTo(origin);
(hasPositions(config.positions)
  ? strategy.withPositions(config.positions)
  : strategy.withPositions(DEFAULT_POSITIONS);

Type guard

function isNonEmptyPositionArray(v: unknown): v is ConnectedPosition[] {
  return Array.isArray(v) && v.length > 0 &&
    v.every(p => p && 'originX' in p && 'originY' in p && 'overlayX' in p && 'overlayY' in p);
}

Try / catch

try {
  overlayRef.attach(panel, strategy);
} catch (e) {
  if (e.message.includes('At least one position is required')) {
    strategy.withPositions(DEFAULT_POSITIONS);
    overlayRef.attach(panel, strategy);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling `overlay.position().flexibleConnectedTo(origin)` and passing an empty array to `withPositions([])`, or constructing the strategy with an empty positions list and never calling `withPositions` before the overlay attaches.

Common situations: Driving positions from dynamic/config-driven data that ends up as an empty array (e.g. an empty JSON config, filtered list); spreading an undefined/null positions variable into withPositions; forgetting the withDefaultPosition/withPositions call after refactor.

Related errors


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