angular/components · error
Could not find a tree control, levelAccessor, or childrenAcc
Error message
Could not find a tree control, levelAccessor, or childrenAccessor for the tree.
What it means
On init, _checkTreeControlUsage counts how many navigation mechanisms the tree was given: treeControl input, levelAccessor input, or childrenAccessor input. If none is present, the tree has no way to traverse/flatten data and getTreeControlMissingError() is thrown. Exactly one mechanism must be provided.
Source
Thrown at src/cdk/tree/tree.ts:505
private _checkTreeControlUsage() {
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.View on GitHub (pinned to 0411926e7d)
Solutions
- Provide exactly one traversal config: pass [treeControl]="treeControl" (FlatTreeControl or NestedTreeControl) to the tree.
- Alternatively, for accessor-based trees, pass [levelAccessor]="node => node.level" or [childrenAccessor]="node => node.children".
- Check the binding is applied to the tree element itself (not a nested node template) and that spelling/case is correct.
- If the tree is rendered conditionally, make sure the config object is created before the component initializes (ngOnInit).
Example fix
// before
<cdk-tree [dataSource]="dataSource">
<cdk-tree-node *cdkTreeNodeDef="let node">{{node.name}}</cdk-tree-node>
</cdk-tree>
// after
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
<cdk-tree-node *cdkTreeNodeDef="let node">{{node.name}}</cdk-tree-node>
</cdk-tree>
// or accessor style:
<cdk-tree [dataSource]="dataSource" [levelAccessor]="getLevel">... Defensive patterns
Strategy: validation
Validate before calling
const controlCount = [+!!(tree as any).treeControl, +!!(tree as any).levelAccessor, +!!(tree as any).childrenAccessor]
.reduce((a, b) => a + b, 0);
if (controlCount === 0) console.error('cdk-tree needs exactly one of treeControl, levelAccessor, childrenAccessor'); Type guard
function hasTraversalConfig(t: { treeControl?: unknown; levelAccessor?: unknown; childrenAccessor?: unknown }): boolean {
return !!t.treeControl !== !!t.levelAccessor !== !!t.childrenAccessor ||
(!!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('tree control, levelAccessor, or childrenAccessor')) {
this.tree.treeControl = this.treeControl; // provide default
} else { throw e; }
} Prevention
- Create the FlatTreeControl/NestedTreeControl in the component constructor so it exists before first render.
- Standardize on one approach (treeControl OR accessors) per codebase to avoid omissions.
- Wrap reusable tree templates in a wrapper component that enforces required inputs via @Input({ required: true }).
- Smoke-test every tree in dev mode; the error only fires with ngDevMode truthy — keep dev assertions enabled.
When it happens
Trigger: ngOnInit of a <cdk-tree> where none of [treeControl], [levelAccessor], [childrenAccessor] inputs are bound — e.g. <cdk-tree [dataSource]="data"><cdk-tree-node ...>.
Common situations: Copy-pasting a tree template but forgetting the treeControl binding; upgrading Angular CDK (v13+ introduced levelAccessor/childrenAccessor) and removing an old control without adding accessors; building a minimal tree to experiment and omitting all config; typos like [treecontrol].
Related errors
- A valid data source must be provided.
- More than one of tree control, levelAccessor, or childrenAcc
- 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/f9768273a41ad827.
Report an issue: GitHub.