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
- Move the trigger outside the <mat-menu> content and attach it via matMenuTriggerFor.
- 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).
- 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
- Give every nesting level its own #menu template reference variable
- Never reuse a single menu instance for parent and child levels
- Keep triggers outside <mat-menu> content unless building the documented nested-menu pattern pointing at a DIFFERENT child menu
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
- MatSelectionColumn: missing MatSelection in the parent
- MatSelectionColumn: missing parent table
- xPosition value must be either 'before' or after'. Exa
- yPosition value must be either 'above' or below'. Exam
- mat-tab-group background color must be set through the Sass
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/312430294c3dd00e.
Report an issue: GitHub.