angular/components · warning

Tree is using conflicting node types which can cause unexpec

Error message

Tree is using conflicting node types which can cause unexpected behavior. Please use tree nodes of the same type (e.g. only flat or only nested). Current node type: "${currentType}", new node type "${newType}".

What it means

A CDK tree can be built with either flat nodes (`cdk-flat-tree`-style, `TreeFlattener`) or nested nodes (recursive `cdk-nested-tree`), but not both at once. `_setNodeTypeIfUnset` records the first node type seen and warns in dev mode when a second, different type appears, because mixing them causes undefined rendering/selection behavior.

Source

Thrown at src/cdk/tree/tree.ts:331

    if (defaultNodeDefs.length > 1 && (typeof ngDevMode === 'undefined' || ngDevMode)) {
      throw getTreeMultipleDefaultNodeDefsError();
    }
    this._defaultNodeDef = defaultNodeDefs[0];
  }

  /**
   * Sets the node type for the tree, if it hasn't been set yet.
   *
   * This will be called by the first node that's rendered in order for the tree
   * to determine what data transformations are required.
   */
  _setNodeTypeIfUnset(newType: 'flat' | 'nested') {
    const currentType = this._nodeType.value;

    if (currentType === null) {
      this._nodeType.next(newType);
    } else if ((typeof ngDevMode === 'undefined' || ngDevMode) && currentType !== newType) {
      console.warn(
        `Tree is using conflicting node types which can cause unexpected behavior. ` +
          `Please use tree nodes of the same type (e.g. only flat or only nested). ` +
          `Current node type: "${currentType}", new node type "${newType}".`,
      );
    }
  }

  /**
   * Switch to the provided data source by resetting the data and unsubscribing from the current
   * render change subscription if one exists. If the data source is null, interpret this by
   * clearing the node outlet. Otherwise start listening for new data.
   */
  private _switchDataSource(dataSource: DataSource<T> | Observable<T[]> | T[]) {
    if (this._dataSource && typeof (this._dataSource as DataSource<T>).disconnect === 'function') {
      (this.dataSource as DataSource<T>).disconnect(this);
    }

    this._dataSubscription?.unsubscribe();

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Use only one node type per tree: all `<mat-tree-node>` for flat trees, all `<mat-nested-tree-node>` for nested trees.
  2. Delete leftover templates of the other node type from the tree's template.
  3. If you need both forms, render two separate `mat-tree` components.

Example fix

<!-- before -->
<mat-tree [dataSource]="dataSource">
  <mat-tree-node *matTreeNodeDef="let node">{{node.name}}</mat-tree-node>
  <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">...</mat-nested-tree-node>
</mat-tree>

<!-- after (nested tree) -->
<mat-tree [dataSource]="dataSource">
  <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">...</mat-nested-tree-node>
</mat-tree>
Defensive patterns

Strategy: validation

Validate before calling

// audit tree templates in CI
const flat = /<(mat|cdk)-tree-node/.test(tpl);
const nested = /<(mat|cdk)-nested-tree-node/.test(tpl);
if (flat && nested) throw new Error('Mixed tree node types in ' + file);

Prevention

When it happens

Trigger: Declaring `mat-tree` children that mix `<mat-tree-node>` (flat) with `<mat-nested-tree-node>` (nested), or conditionally rendering both node kinds in the same tree template.

Common situations: Refactoring a flat tree to nested (or vice versa) and leaving old node templates in place; copying node templates between trees; dynamic template switches based on data.

Related errors


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