{"record":{"id":"c963d266e226e93a","repo":"angular/components","slug":"more-than-one-of-tree-control-levelaccessor-or-c","errorCode":null,"errorMessage":"More than one of tree control, levelAccessor, or childrenAccessor were provided.","messagePattern":"More than one of tree control, levelAccessor, or childrenAccessor were provided\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cdk/tree/tree.ts","lineNumber":507,"sourceCode":"    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.\n    //\n    // However, if change detection is called while the component's view is","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/tree/tree.ts#L489-L525","documentation":"_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.","triggerScenarios":"ngOnInit with bindings like <cdk-tree [treeControl]=\"ctrl\" [levelAccessor]=\"fn\">, or [levelAccessor] together with [childrenAccessor], or [treeControl] plus [childrenAccessor].","commonSituations":"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.","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."],"exampleFix":"// before\n<cdk-tree [dataSource]=\"ds\" [treeControl]=\"treeControl\" [levelAccessor]=\"node => node.level\">...\n\n// after\n<cdk-tree [dataSource]=\"ds\" [levelAccessor]=\"node => node.level\">...","handlingStrategy":"validation","validationCode":"const provided = [\n  (tree as any).treeControl,\n  (tree as any).levelAccessor,\n  (tree as any).childrenAccessor\n].filter(Boolean).length;\nif (provided > 1) console.error('cdk-tree accepts only ONE of treeControl, levelAccessor, childrenAccessor');","typeGuard":"function hasExactlyOneTraversal(t: { treeControl?: unknown; levelAccessor?: unknown; childrenAccessor?: unknown }): boolean {\n  return ((!!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('More than one of tree control')) {\n    this.tree.levelAccessor = undefined; // drop the redundant accessor\n  } else { throw e; }\n}","preventionTips":["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."],"tags":["angular","cdk-tree","tree-control","configuration-conflict"],"backgroundTag":"conflicting-tree-config","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}