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
- Wrap the lookup in try/except UnknownNodeID and handle absence gracefully.
- Re-derive IDs after clear()/reload; prefer stable business keys in node.data rather than NodeIDs.
- 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
- Look up nodes immediately when events arrive
- Keep stable keys in node.data and rebuild lookups after clear()
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
- No child found with id={id!r}
- No immediate child of type {expect_type}; {self._nodes}
- Unable to add a node both before and after a node
- The node specified for `before` is not a child of this node
- `before` argument must be an index or a TreeNode object to a
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/0cd238fdcc5ddd32.
Report an issue: GitHub.