{"record":{"id":"f9768273a41ad827","repo":"angular/components","slug":"could-not-find-a-tree-control-levelaccessor-or-c","errorCode":null,"errorMessage":"Could not find a tree control, levelAccessor, or childrenAccessor for the tree.","messagePattern":"Could not find a tree control, levelAccessor, or childrenAccessor for the tree\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cdk/tree/tree.ts","lineNumber":505,"sourceCode":"\n  private _checkTreeControlUsage() {\n    if (typeof ngDevMode === 'undefined' || ngDevMode) {\n      // Verify that Tree follows API contract of using one of TreeControl, levelAccessor or\n      // childrenAccessor. Throw an appropriate error if contract is not met.\n      let numTreeControls = 0;\n\n      if (this.treeControl) {\n        numTreeControls++;\n      }\n      if (this.levelAccessor) {\n        numTreeControls++;\n      }\n      if (this.childrenAccessor) {\n        numTreeControls++;\n      }\n\n      if (!numTreeControls) {\n        throw getTreeControlMissingError();\n      } else if (numTreeControls > 1) {\n        throw getMultipleTreeControlsError();\n      }\n    }\n  }\n\n  /** Check for changes made in the data and render each change (node added/removed/moved). */\n  renderNodeChanges(\n    data: readonly T[],\n    dataDiffer: IterableDiffer<T> = this._dataDiffer,\n    viewContainer: ViewContainerRef = this._nodeOutlet.viewContainer,\n    parentData?: T,\n  ) {\n    const changes = dataDiffer.diff(data);\n\n    // Some tree consumers expect change detection to propagate to nodes\n    // even when the array itself hasn't changed; we explicitly detect changes\n    // anyways in order for nodes to update their data.","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/tree/tree.ts#L487-L523","documentation":"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.","triggerScenarios":"ngOnInit of a <cdk-tree> where none of [treeControl], [levelAccessor], [childrenAccessor] inputs are bound — e.g. <cdk-tree [dataSource]=\"data\"><cdk-tree-node ...>.","commonSituations":"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].","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)."],"exampleFix":"// before\n<cdk-tree [dataSource]=\"dataSource\">\n  <cdk-tree-node *cdkTreeNodeDef=\"let node\">{{node.name}}</cdk-tree-node>\n</cdk-tree>\n\n// after\n<cdk-tree [dataSource]=\"dataSource\" [treeControl]=\"treeControl\">\n  <cdk-tree-node *cdkTreeNodeDef=\"let node\">{{node.name}}</cdk-tree-node>\n</cdk-tree>\n// or accessor style:\n<cdk-tree [dataSource]=\"dataSource\" [levelAccessor]=\"getLevel\">...","handlingStrategy":"validation","validationCode":"const controlCount = [+!!(tree as any).treeControl, +!!(tree as any).levelAccessor, +!!(tree as any).childrenAccessor]\n  .reduce((a, b) => a + b, 0);\nif (controlCount === 0) console.error('cdk-tree needs exactly one of treeControl, levelAccessor, childrenAccessor');","typeGuard":"function hasTraversalConfig(t: { treeControl?: unknown; levelAccessor?: unknown; childrenAccessor?: unknown }): boolean {\n  return !!t.treeControl !== !!t.levelAccessor !== !!t.childrenAccessor ||\n    (!!t.treeControl ? 1 : 0) + (!!t.levelAccessor ? 1 : 0) + (!!t.childrenAccessor ? 1 : 0) === 1;\n}","tryCatchPattern":"try {\n  this.tree.ngOnInit();\n} catch (e) {\n  if (e && String(e.message).includes('tree control, levelAccessor, or childrenAccessor')) {\n    this.tree.treeControl = this.treeControl; // provide default\n  } else { throw e; }\n}","preventionTips":["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."],"tags":["angular","cdk-tree","tree-control","missing-input"],"backgroundTag":"missing-tree-control","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}