angular/components · error

matMenuTriggerFor: menu cannot contain its own trigger. Assi

Error message

matMenuTriggerFor: menu cannot contain its own trigger. Assign a menu that is not a parent of the trigger or move the trigger outside of the menu.

What it means

A matMenuTrigger's menu must not be a DOM ancestor of the trigger itself; that would nest a trigger inside its own content, which the CDK overlay/menu wiring cannot represent. When the _menu setter detects the trigger element is inside the assigned menu content, it calls throwMatMenuRecursiveError.

Source

Thrown at src/material/menu/menu-errors.ts:35

}

/**
 * Throws an exception for the case when menu's y-position value isn't valid.
 * In other words, it doesn't match 'above' or 'below'.
 * @docs-private
 */
export function throwMatMenuInvalidPositionY() {
  throw Error(`yPosition value must be either 'above' or below'.
      Example: <mat-menu yPosition="above" #menu="matMenu"></mat-menu>`);
}

/**
 * Throws an exception for the case when a menu is assigned
 * to a trigger that is placed inside the same menu.
 * @docs-private
 */
export function throwMatMenuRecursiveError() {
  throw Error(
    `matMenuTriggerFor: menu cannot contain its own trigger. Assign a menu that is ` +
      `not a parent of the trigger or move the trigger outside of the menu.`,
  );
}

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Move the trigger outside the <mat-menu> content and attach it via matMenuTriggerFor.
  2. For cascading submenus, use the dedicated MatMenu nested-menu pattern: place the child trigger inside the parent menu and point it at a DIFFERENT child menu (matMenuTriggerFor pointing to a separate mat-menu instance).
  3. Never reuse one menu instance as both parent and child; create one mat-menu per level.

Example fix

// before
<mat-menu #menu="matMenu">
  <button mat-menu-item [matMenuTriggerFor]="menu">Nested</button>
</mat-menu>
// after
<mat-menu #parent="matMenu">
  <button mat-menu-item [matMenuTriggerFor]="child">Nested</button>
</mat-menu>
<mat-menu #child="matMenu">
  <button mat-menu-item>Action</button>
</mat-menu>
<button [matMenuTriggerFor]="parent">Open</button>
Defensive patterns

Strategy: validation

Validate before calling

// Assert no trigger lives inside its own menu before rendering:
const menuEl = document.querySelector('mat-menu');
const recursive = menuEl &&
  Array.from(menuEl.querySelectorAll('[matMenuTriggerFor]'))
    .some(t => t.getAttribute('matMenuTriggerFor') === menuRef);
if (recursive) {
  throw new Error('Menu contains its own trigger; restructure nested menus');
}

Try / catch

try {
  this.trigger.menu = candidateMenu;
} catch (e) {
  if ((e as Error).message.includes('menu cannot contain its own trigger')) {
    console.error('Create a separate child mat-menu instance for nested levels');
  } else { throw e; }
}

Prevention

When it happens

Trigger: Placing a button with [matMenuTriggerFor]="menu" inside a <mat-menu> that references that same menu (or an ancestor menu) as its content — e.g. building nested menus by reusing one menu instance for parent and child.

Common situations: Attempting multi-level cascading menus with a single shared menu component; recursive template components where each level declares trigger+menu with the same reference; moving a trigger into a menu panel during refactors.

Related errors


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