{"record":{"id":"dc9aa94f40f58e71","repo":"angular/components","slug":"flexibleconnectedpositionstrategy-at-least-one-po","errorCode":null,"errorMessage":"FlexibleConnectedPositionStrategy: At least one position is required.","messagePattern":"FlexibleConnectedPositionStrategy: At least one position is required\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cdk/overlay/position/flexible-connected-position-strategy.ts","lineNumber":1202,"sourceCode":"    return !this._hasFlexibleDimensions || this._isPushed;\n  }\n\n  /** Retrieves the offset of a position along the x or y axis. */\n  private _getOffset(position: ConnectedPosition, axis: 'x' | 'y') {\n    if (axis === 'x') {\n      // We don't do something like `position['offset' + axis]` in\n      // order to avoid breaking minifiers that rename properties.\n      return position.offsetX == null ? this._offsetX : position.offsetX;\n    }\n\n    return position.offsetY == null ? this._offsetY : position.offsetY;\n  }\n\n  /** Validates that the current position match the expected values. */\n  private _validatePositions(): void {\n    if (typeof ngDevMode === 'undefined' || ngDevMode) {\n      if (!this._preferredPositions.length) {\n        throw Error('FlexibleConnectedPositionStrategy: At least one position is required.');\n      }\n\n      // TODO(crisbeto): remove these once Angular's template type\n      // checking is advanced enough to catch these cases.\n      this._preferredPositions.forEach(pair => {\n        validateHorizontalPosition('originX', pair.originX);\n        validateVerticalPosition('originY', pair.originY);\n        validateHorizontalPosition('overlayX', pair.overlayX);\n        validateVerticalPosition('overlayY', pair.overlayY);\n      });\n    }\n  }\n\n  /** Adds a single CSS class or an array of classes on the overlay panel. */\n  private _addPanelClasses(cssClasses: string | string[]) {\n    if (this._pane) {\n      coerceArray(cssClasses).forEach(cssClass => {\n        if (cssClass !== '' && this._appliedPanelClasses.indexOf(cssClass) === -1) {","sourceCodeStart":1184,"sourceCodeEnd":1220,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/overlay/position/flexible-connected-position-strategy.ts#L1184-L1220","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst strategy = overlay.position().flexibleConnectedTo(origin).withPositions(myPositions);\n// after\nconst positions = myPositions?.length ? myPositions : [{originX:'start', originY:'bottom', overlayX:'start', overlayY:'top'}];\nconst strategy = overlay.position().flexibleConnectedTo(origin).withPositions(positions);","handlingStrategy":"validation","validationCode":"function hasPositions(positions) { return Array.isArray(positions) && positions.length > 0; }\nconst strategy = overlay.position().flexibleConnectedTo(origin);\n(hasPositions(config.positions)\n  ? strategy.withPositions(config.positions)\n  : strategy.withPositions(DEFAULT_POSITIONS);","typeGuard":"function isNonEmptyPositionArray(v: unknown): v is ConnectedPosition[] {\n  return Array.isArray(v) && v.length > 0 &&\n    v.every(p => p && 'originX' in p && 'originY' in p && 'overlayX' in p && 'overlayY' in p);\n}","tryCatchPattern":"try {\n  overlayRef.attach(panel, strategy);\n} catch (e) {\n  if (e.message.includes('At least one position is required')) {\n    strategy.withPositions(DEFAULT_POSITIONS);\n    overlayRef.attach(panel, strategy);\n  } else { throw e; }\n}","preventionTips":["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"],"tags":["angular","cdk","overlay","positioning"],"backgroundTag":"empty-preferred-positions","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}