angular/components · error · Error

Dialog with id "${config.id}" exists already. The dialog id

Error message

Dialog with id "${config.id}" exists already. The dialog id must be unique.

What it means

Dialog.open(config) rejects a custom config.id that is already in use by an open dialog. Because duplicate ids would break getDialogById and afterClosed lookups, the CDK throws when `config.id` matches an existing dialog's id. This check only runs in development (ngDevMode) but the constraint holds in production too.

Source

Thrown at src/cdk/dialog/dialog.ts:132

  ): DialogRef<R, C>;

  open<R = unknown, D = unknown, C = unknown>(
    componentOrTemplateRef: ComponentType<C> | TemplateRef<C>,
    config?: DialogConfig<D, DialogRef<R, C>>,
  ): DialogRef<R, C> {
    const defaults = (this._defaultOptions || new DialogConfig()) as DialogConfig<
      D,
      DialogRef<R, C>
    >;
    config = {...defaults, ...config};
    config.id = config.id || this._idGenerator.getId('cdk-dialog-');

    if (
      config.id &&
      this.getDialogById(config.id) &&
      (typeof ngDevMode === 'undefined' || ngDevMode)
    ) {
      throw Error(`Dialog with id "${config.id}" exists already. The dialog id must be unique.`);
    }

    const overlayConfig = this._getOverlayConfig(config);
    const overlayRef = createOverlayRef(this._injector, overlayConfig);
    const dialogRef = new DialogRef(overlayRef, config);
    const dialogContainer = this._attachContainer(overlayRef, dialogRef, config);

    (dialogRef as {containerInstance: DialogContainer}).containerInstance = dialogContainer;

    // If this is the first dialog that we're opening, hide all the non-overlay content.
    if (!this.openDialogs.length) {
      // Resolve this ahead of time, because some internal apps
      // mock it out and depend on it being synchronous.
      const overlayContainer = this._overlayContainer.getContainerElement();

      if (dialogContainer._focusTrapped) {
        dialogContainer._focusTrapped.pipe(take(1)).subscribe(() => {
          this._hideNonDialogContentFromAssistiveTechnology(overlayContainer);

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Close the existing dialog first: find it via `this.dialog.getDialogById('my-id')?.close()` before opening.
  2. Generate unique ids at runtime, e.g. `id: 'dialog-' + Math.random().toString(36).slice(2)` or a counter/UUID.
  3. Reuse the existing dialog: if getDialogById returns one, focus/interact with it instead of opening a new one.
  4. Omit the `id` property if you don't need deterministic lookup — Angular CDK assigns an auto id.
  5. Track open state in a service so the same id is never passed while open.

Example fix

// before
this.dialog.open(SettingsComponent, { id: 'settings' });
// after
const existing = this.dialog.getDialogById('settings');
if (existing) {
  existing.close();
}
this.dialog.open(SettingsComponent, { id: 'settings' });
Defensive patterns

Strategy: validation

Validate before calling

const id = 'settings';
if (dialog.getDialogById(id)) {
  dialog.getDialogById(id)!.close(); // or return early
}
dialog.open(SettingsComponent, { id });

Type guard

function isOpen(dialog: Dialog, id: string): boolean {
  return !!dialog.getDialogById(id);
}

Try / catch

try {
  return dialog.open(Comp, { id: 'fixed-id' });
} catch (e) {
  if ((e as Error).message.includes('exists already')) {
    return dialog.getDialogById('fixed-id')!;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling this.dialog.open(SomeComponent, { id: 'settings' }) while a dialog with id 'settings' is still open; rendering two dialogs with the same hardcoded id, e.g. in a component rendered twice.

Common situations: Same dialog component instantiated in two places (list rows, tabs) each passing a shared static id; forgetting to close a dialog before reopening it with the same id; HMR or router re-entry leaving the old dialog open.

Related errors


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