Textualize/textual · error · UnknownNodeID

Unknown NodeID ({node_id}) in tree

Error message

Unknown NodeID ({node_id}) in tree

What it means

UnknownNodeID from Tree.get_node_by_id(): the requested NodeID is not present in the tree's node registry (self._tree_nodes). The node was likely removed or the tree cleared/reloaded, regenerating IDs.

Source

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

        else:
            return line.node

    def get_node_by_id(self, node_id: NodeID) -> TreeNode[TreeDataType]:
        """Get a tree node by its ID.

        Args:
            node_id: The ID of the node to get.

        Returns:
            The node associated with that ID.

        Raises:
            UnknownNodeID: Raised if the `TreeNode` ID is unknown.
        """
        try:
            return self._tree_nodes[node_id]
        except KeyError:
            raise UnknownNodeID(f"Unknown NodeID ({node_id}) in tree") from None

    def validate_cursor_line(self, value: int) -> int:
        """Prevent cursor line from going outside of range.

        Args:
            value: The value to test.

        Return:
            A valid version of the given value.
        """
        return clamp(value, 0, len(self._tree_lines) - 1)

    def validate_guide_depth(self, value: int) -> int:
        """Restrict guide depth to reasonable range.

        Args:
            value: The value to test.

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Wrap the lookup in try/except UnknownNodeID and handle absence gracefully.
  2. Re-derive IDs after clear()/reload; prefer stable business keys in node.data rather than NodeIDs.
  3. Check `tree._tree_nodes` membership or use `get_node_by_id` immediately when the event arrives.

Example fix

# before
node = tree.get_node_by_id(saved_id)
# after
try:
    node = tree.get_node_by_id(saved_id)
except UnknownNodeID:
    node = None
Defensive patterns

Strategy: try-catch

Validate before calling

if node_id in tree._tree_nodes:
    node = tree.get_node_by_id(node_id)

Try / catch

from textual.widgets._tree import UnknownNodeID
try:
    node = tree.get_node_by_id(node_id)
except UnknownNodeID:
    node = None

Prevention

When it happens

Trigger: `tree.get_node_by_id(node_id)` after `tree.clear()`, after `node.remove()` on the target, or with an ID captured from a previous build of the tree.

Common situations: Storing NodeIDs in app state (e.g. selection persistence) across tree rebuilds; handling Tree.NodeSelected with a delayed lookup after mutation.

Related errors


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