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
- Pass at least one position object to `withPositions`, e.g. `strategy.withPositions([{originX:'start',originY:'bottom',overlayX:'start',overlayY:'top'}])`
- Use `withDefaultPosition(defaultPos)` before `withPositions` so there is always a fallback
- Guard dynamic position arrays: fall back to a default when the computed array is empty
- 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
- Always define a DEFAULT_POSITIONS constant used as fallback
- Never pass config-derived position arrays without an emptiness check
- Prefer withDefaultPosition() for the primary position, withPositions() only for overrides
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
- ConnectedPosition: Invalid ${property} "${value}". Expected
- ConnectedPosition: Invalid ${property} "${value}". Expected
- This position strategy is already attached to an overlay
- Scroll strategy has already been attached.
- Scroll strategy has already been attached.
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/dc9aa94f40f58e71.
Report an issue: GitHub.