{"record":{"id":"2d724946f6e7279f","repo":"Textualize/textual","slug":"attempt-to-remove-the-root-node-of-a-tree","errorCode":null,"errorMessage":"Attempt to remove the root node of a Tree.","messagePattern":"Attempt to remove the root node of a Tree\\.","errorType":"exception","errorClass":"RemoveRootError","httpStatus":null,"severity":"error","filePath":"src/textual/widgets/_tree.py","lineNumber":501,"sourceCode":"        \"\"\"Remove the current node and all its children.\n\n        Note:\n            This is the internal support method for `remove`. Call `remove`\n            to ensure the tree gets refreshed.\n        \"\"\"\n        self._remove_children()\n        assert self._parent is not None\n        del self._parent._children[self._parent._children.index(self)]\n        del self._tree._tree_nodes[self.id]\n\n    def remove(self) -> None:\n        \"\"\"Remove this node from the tree.\n\n        Raises:\n            RemoveRootError: If there is an attempt to remove the root.\n        \"\"\"\n        if self.is_root:\n            raise RemoveRootError(\"Attempt to remove the root node of a Tree.\")\n        self._remove()\n        self._tree._invalidate()\n\n    def remove_children(self) -> None:\n        \"\"\"Remove any child nodes of this node.\"\"\"\n        self._remove_children()\n        self._tree._invalidate()\n\n    def refresh(self) -> None:\n        \"\"\"Initiate a refresh (repaint) of this node.\"\"\"\n        self._updates += 1\n        self._tree._refresh_line(self._line)\n\n\nclass Tree(Generic[TreeDataType], ScrollView, can_focus=True):\n    \"\"\"A widget for displaying and navigating data in a tree.\"\"\"\n\n    ICON_NODE = \"▶ \"","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/widgets/_tree.py#L483-L519","documentation":"RemoveRootError from TreeNode.remove(): the Tree widget's root node is structural and cannot be removed; removing it would leave the widget in an invalid state. Use root.remove_children() to empty the tree instead.","triggerScenarios":"Calling `tree.root.remove()` directly, or iterating `tree.iter_expanded_nodes()`/walk and calling remove() without excluding the root.","commonSituations":"Generic deletion loops that call `.remove()` on every node; attempting to clear the tree by removing root instead of `tree.clear()`.","solutions":["Use `tree.clear()` to reset the whole tree.","Use `tree.root.remove_children()` to keep the root but delete contents.","Guard loops with `if not node.is_root: node.remove()`."],"exampleFix":"# before\ntree.root.remove()\n# after\ntree.clear()  # or tree.root.remove_children()","handlingStrategy":"validation","validationCode":"if node.is_root:\n    tree.clear()\nelse:\n    node.remove()","typeGuard":"from textual.widgets._tree import TreeNode\ndef is_removable(n: TreeNode) -> bool:\n    return not n.is_root","tryCatchPattern":"from textual.widgets._tree import RemoveRootError\ntry:\n    node.remove()\nexcept RemoveRootError:\n    tree.clear()","preventionTips":["Use tree.clear() to empty trees","Filter root in bulk-deletion loops: [n.remove() for n in nodes if not n.is_root]"],"tags":["tree","root-node","remove","textual"],"backgroundTag":"operation-not-allowed","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}