Textualize/textual · error · RemoveRootError

Attempt to remove the root node of a Tree.

Error message

Attempt to remove the root node of a Tree.

What it means

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.

Source

Thrown at src/textual/widgets/_tree.py:501

        """Remove the current node and all its children.

        Note:
            This is the internal support method for `remove`. Call `remove`
            to ensure the tree gets refreshed.
        """
        self._remove_children()
        assert self._parent is not None
        del self._parent._children[self._parent._children.index(self)]
        del self._tree._tree_nodes[self.id]

    def remove(self) -> None:
        """Remove this node from the tree.

        Raises:
            RemoveRootError: If there is an attempt to remove the root.
        """
        if self.is_root:
            raise RemoveRootError("Attempt to remove the root node of a Tree.")
        self._remove()
        self._tree._invalidate()

    def remove_children(self) -> None:
        """Remove any child nodes of this node."""
        self._remove_children()
        self._tree._invalidate()

    def refresh(self) -> None:
        """Initiate a refresh (repaint) of this node."""
        self._updates += 1
        self._tree._refresh_line(self._line)


class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
    """A widget for displaying and navigating data in a tree."""

    ICON_NODE = "▶ "

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Use `tree.clear()` to reset the whole tree.
  2. Use `tree.root.remove_children()` to keep the root but delete contents.
  3. Guard loops with `if not node.is_root: node.remove()`.

Example fix

# before
tree.root.remove()
# after
tree.clear()  # or tree.root.remove_children()
Defensive patterns

Strategy: validation

Validate before calling

if node.is_root:
    tree.clear()
else:
    node.remove()

Type guard

from textual.widgets._tree import TreeNode
def is_removable(n: TreeNode) -> bool:
    return not n.is_root

Try / catch

from textual.widgets._tree import RemoveRootError
try:
    node.remove()
except RemoveRootError:
    tree.clear()

Prevention

When it happens

Trigger: Calling `tree.root.remove()` directly, or iterating `tree.iter_expanded_nodes()`/walk and calling remove() without excluding the root.

Common situations: Generic deletion loops that call `.remove()` on every node; attempting to clear the tree by removing root instead of `tree.clear()`.

Related errors


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/2d724946f6e7279f. Report an issue: GitHub.