angular/components · error · Error

Scroll strategy has already been attached.

Error message

Scroll strategy has already been attached.

What it means

CloseScrollStrategy tracks a single attached OverlayRef; calling `attach` a second time without detaching throws `getMatScrollStrategyAlreadyAttachedError()`. The strategy is designed to be bound to exactly one overlay at a time so scroll-closing behavior is unambiguous.

Source

Thrown at src/cdk/overlay/scroll/close-scroll-strategy.ts:58

/**
 * Strategy that will close the overlay as soon as the user starts scrolling.
 */
export class CloseScrollStrategy implements ScrollStrategy {
  private _scrollSubscription: Subscription | null = null;
  private _overlayRef!: OverlayRef;
  private _initialScrollPosition!: number;

  constructor(
    private _scrollDispatcher: ScrollDispatcher,
    private _ngZone: NgZone,
    private _viewportRuler: ViewportRuler,
    private _config?: CloseScrollStrategyConfig,
  ) {}

  /** Attaches this scroll strategy to an overlay. */
  attach(overlayRef: OverlayRef) {
    if (this._overlayRef && (typeof ngDevMode === 'undefined' || ngDevMode)) {
      throw getMatScrollStrategyAlreadyAttachedError();
    }

    this._overlayRef = overlayRef;
  }

  /** Enables the closing of the attached overlay on scroll. */
  enable() {
    if (this._scrollSubscription) {
      return;
    }

    const stream = this._scrollDispatcher.scrolled(0).pipe(
      filter(scrollable => {
        return (
          !scrollable ||
          !this._overlayRef.overlayElement.contains(scrollable.getElementRef().nativeElement)
        );
      }),

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Create a new CloseScrollStrategy per overlay: `overlay.scrollStrategies.close()` returns a fresh strategy each call
  2. Detach the strategy from the previous overlay before attaching it to a new one
  3. If sharing in a service, store the strategy factory (function) instead of a strategy instance
  4. Call `overlayRef.detach()`/dispose the prior overlay so the strategy can be reused

Example fix

// before
private readonly scrollStrategy = this.overlay.scrollStrategies.close();
open() { this.dialog.open(D, {scrollStrategy: this.scrollStrategy}); } // second open throws
// after
open() { this.dialog.open(D, {scrollStrategy: this.overlay.scrollStrategies.close()}); } // fresh strategy per open
Defensive patterns

Strategy: validation

Validate before calling

import {CloseScrollStrategy} from '@angular/cdk/overlay';
function isFresh(strategy: CloseScrollStrategy): boolean {
  return (strategy as any)._overlayRef == null;
}

Type guard

function canAttach(strategy: CloseScrollStrategy): boolean {
  return (strategy as unknown as {_overlayRef?: unknown})._overlayRef === undefined;
}

Try / catch

try {
  strategy.attach(overlayRef);
} catch (e) {
  if (e.message.includes('Scroll strategy has already been attached')) {
    strategy = overlay.scrollStrategies.close(); // fresh strategy
    strategy.attach(overlayRef);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Reusing a single CloseScrollStrategy instance (e.g. one created via a shared ScrollStrategyFactory or stored in a service) to attach to a second overlay without detaching the first or creating a new strategy.

Common situations: Passing `this.scrollStrategy` (a memoized singleton) to multiple dialogs/overlays; opening two overlays that share one strategy from a provider; re-opening an overlay after it was closed without detaching its strategy.

Related errors


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