angular/components · error
More than one of tree control, levelAccessor, or childrenAcc
Error message
More than one of tree control, levelAccessor, or childrenAccessor were provided.
What it means
_checkTreeControlUsage requires exactly one traversal mechanism. If the tree receives more than one of treeControl, levelAccessor, or childrenAccessor simultaneously, the tree cannot decide how to compute levels/children and getMultipleTreeControlsError() is thrown.
Source
Thrown at src/cdk/tree/tree.ts:507
if (typeof ngDevMode === 'undefined' || ngDevMode) {
// Verify that Tree follows API contract of using one of TreeControl, levelAccessor or
// childrenAccessor. Throw an appropriate error if contract is not met.
let numTreeControls = 0;
if (this.treeControl) {
numTreeControls++;
}
if (this.levelAccessor) {
numTreeControls++;
}
if (this.childrenAccessor) {
numTreeControls++;
}
if (!numTreeControls) {
throw getTreeControlMissingError();
} else if (numTreeControls > 1) {
throw getMultipleTreeControlsError();
}
}
}
/** Check for changes made in the data and render each change (node added/removed/moved). */
renderNodeChanges(
data: readonly T[],
dataDiffer: IterableDiffer<T> = this._dataDiffer,
viewContainer: ViewContainerRef = this._nodeOutlet.viewContainer,
parentData?: T,
) {
const changes = dataDiffer.diff(data);
// Some tree consumers expect change detection to propagate to nodes
// even when the array itself hasn't changed; we explicitly detect changes
// anyways in order for nodes to update their data.
//
// However, if change detection is called while the component's view isView on GitHub (pinned to 0411926e7d)
Solutions
- Remove all but one of [treeControl], [levelAccessor], [childrenAccessor] from the tree element.
- If moving to the accessor API, delete the [treeControl] binding and use [levelAccessor] or [childrenAccessor].
- If the bindings come from a shared template/component, gate them with *ngIf or conditional property binding so only one is ever set.
- Search the template for duplicate bindings spread across the element or inherited via ngTemplateOutlet context.
Example fix
// before <cdk-tree [dataSource]="ds" [treeControl]="treeControl" [levelAccessor]="node => node.level">... // after <cdk-tree [dataSource]="ds" [levelAccessor]="node => node.level">...
Defensive patterns
Strategy: validation
Validate before calling
const provided = [
(tree as any).treeControl,
(tree as any).levelAccessor,
(tree as any).childrenAccessor
].filter(Boolean).length;
if (provided > 1) console.error('cdk-tree accepts only ONE of treeControl, levelAccessor, childrenAccessor'); Type guard
function hasExactlyOneTraversal(t: { treeControl?: unknown; levelAccessor?: unknown; childrenAccessor?: unknown }): boolean {
return ((!!t.treeControl ? 1 : 0) + (!!t.levelAccessor ? 1 : 0) + (!!t.childrenAccessor ? 1 : 0)) === 1;
} Try / catch
try {
this.tree.ngOnInit();
} catch (e) {
if (e && String(e.message).includes('More than one of tree control')) {
this.tree.levelAccessor = undefined; // drop the redundant accessor
} else { throw e; }
} Prevention
- During treeControl→accessor migration, delete the old [treeControl] binding in the same commit that adds accessors.
- Lint templates (angular template lint) or code-review for duplicate config bindings on <cdk-tree>.
- Encapsulate tree config in one wrapper component exposing a single mode input (flat | nested).
- Avoid spreading uncontrolled attribute objects onto tree elements.
When it happens
Trigger: ngOnInit with bindings like <cdk-tree [treeControl]="ctrl" [levelAccessor]="fn">, or [levelAccessor] together with [childrenAccessor], or [treeControl] plus [childrenAccessor].
Common situations: Migrating from treeControl-based code to the newer accessor API (CDK 13+) and adding accessors without removing the old treeControl; merging template snippets from two examples; spreading a dynamic attributes object that contains both configs; an abstract tree base class that always sets treeControl while subclasses add accessors.
Related errors
- Could not find a tree control, levelAccessor, or childrenAcc
- A valid data source must be provided.
- Could not find a matching node definition for the provided n
- Cannot retrieve popup content because the combobox is closed
- Could not find tab matching filters: ${JSON.stringify(filter
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/c963d266e226e93a.
Report an issue: GitHub.