# How to Remove a Node and Its Subtree in treelib: A Complete Guide

> Learn to remove a node and its subtree in treelib using remove_node. This guide covers deleting nodes and descendants efficiently in your tree structure.

- Repository: [Xiaming Chen/treelib](https://github.com/caesar0301/treelib)
- Tags: how-to-guide
- Published: 2026-02-26

---

**Use `tree.remove_node(node_id)` to delete a node and all its descendants in caesar0301/treelib, which returns the count of removed nodes and raises `NodeIDAbsentError` if the identifier does not exist.**

The `treelib` library provides a pure Python implementation for managing tree data structures. When working with hierarchical data in the `caesar0301/treelib` repository, you often need to prune entire branches by removing a parent node along with all its children. This guide explains the internal mechanics and practical usage of the subtree removal methods.

## Understanding the `remove_node` Method

The primary method for deleting a node and its subtree is `remove_node`, implemented in **[`treelib/tree.py`](https://github.com/caesar0301/treelib/blob/main/treelib/tree.py)** (lines 1602‑1628). This method performs a complete teardown of the target branch while maintaining tree integrity.

### How It Works Internally

When you call `tree.remove_node(identifier)`, the method executes the following steps according to the source code:

1. **Validation**: Checks that the identifier exists in the internal `_nodes` dictionary. If not, it raises `NodeIDAbsentError` (lines 1606‑1608).
2. **Subtree Collection**: Uses `Tree.expand_tree(identifier)` to gather the target node identifier plus every descendant identifier in the subtree.
3. **Pointer Cleanup**: Clears the backward pointer (`_bpointer`) of each removed node and disconnects children from their parents.
4. **Node Deletion**: Removes all collected identifiers from the internal `_nodes` dictionary.
5. **Parent Update**: Updates the parent node's forward pointer (`_fpointer`) to remove references to the deleted branch.

### Return Value and Error Handling

The method returns an **integer** representing the total number of nodes removed, including the root of the deleted branch. If you attempt to remove a non-existent node, the library raises `NodeIDAbsentError` with a descriptive message indicating the missing identifier.

## Removing a Node and Its Subtree: Code Examples

### Basic Node Removal

The following example demonstrates removing a single node and its subtree, adapted from **[`examples/getting_started.py`](https://github.com/caesar0301/treelib/blob/main/examples/getting_started.py)** (lines 51‑55):

```python
from treelib import Tree

tree = Tree()
tree.create_node("Root", "root")
tree.create_node("Bob", "bob", parent="root")
tree.create_node("Alice", "alice", parent="root")

# Before removal

tree.show()

# └── Root

#     ├── Bob

#     └── Alice

removed_count = tree.remove_node("bob")
print(f"Removed {removed_count} node(s)")

# After removal

tree.show()

# └── Root

#     └── Alice

```

### Removing a Deep Branch

When removing a node with multiple levels of descendants, `remove_node` automatically calculates the total count:

```python

# Create a deeper hierarchy

tree.create_node("Grandpa", "grandpa")
tree.create_node("Dad", "dad", parent="grandpa")
tree.create_node("Mom", "mom", parent="grandpa")
tree.create_node("Child1", "c1", parent="dad")
tree.create_node("Child2", "c2", parent="dad")
tree.create_node("Sibling", "sib", parent="mom")

# Remove the entire "dad" branch (dad + c1 + c2)

count = tree.remove_node("dad")
print(f"Deleted {count} nodes")  # Output: Deleted 3 nodes

```

### Handling Non-Existent Nodes

Always wrap removal operations in try-except blocks to handle missing identifiers gracefully:

```python
from treelib.exceptions import NodeIDAbsentError

try:
    tree.remove_node("nonexistent")
except NodeIDAbsentError as exc:
    print(exc)  # Output: Node 'nonexistent' is not in the tree

```

## Alternative: Using `remove_subtree` to Detach Branches

If you need to **preserve the removed branch** for re-insertion elsewhere rather than permanently deleting it, use `remove_subtree`. Implemented in **[`treelib/tree.py`](https://github.com/caesar0301/treelib/blob/main/treelib/tree.py)** (lines 1629‑1650), this method detaches the node and its descendants into a new `Tree` instance:

```python

# Detach "mom" and her descendants into a separate tree

subtree = tree.remove_subtree("mom")
print(f"Subtree contains {subtree.size()} nodes")

# The original tree no longer contains "mom" or "sib"

# The subtree variable now holds an independent Tree object

```

Unlike `remove_node`, which returns an integer count, `remove_subtree` returns a fully functional `Tree` object containing the detached hierarchy. This is useful for moving branches between trees or archiving deleted sections.

## Summary

- Use **`tree.remove_node(identifier)`** to permanently delete a node and all its descendants in `caesar0301/treelib`.
- The method returns the **count of removed nodes** and automatically updates parent-child relationships in the internal `_nodes` dictionary.
- Implementation resides in **[`treelib/tree.py`](https://github.com/caesar0301/treelib/blob/main/treelib/tree.py)** (lines 1602‑1628), utilizing `expand_tree` to collect subtree identifiers.
- Use **`remove_subtree`** instead if you need to detach a branch into a new `Tree` object rather than deleting it.
- Always handle `NodeIDAbsentError` when removing nodes that might not exist.

## Frequently Asked Questions

### What happens to child nodes when I remove a parent node?

When you call `remove_node` on a parent, the method automatically removes **all descendants** in the subtree. According to the implementation in [`treelib/tree.py`](https://github.com/caesar0301/treelib/blob/main/treelib/tree.py), the method uses `expand_tree` to collect every node identifier in the target branch, then deletes them all from the internal `_nodes` dictionary while clearing their backward pointers.

### How do I check if a node exists before removing it?

You can verify existence using the `tree.contains(identifier)` method or by checking the internal `_nodes` dictionary, but the most Pythonic approach is to catch the `NodeIDAbsentError` exception. The `remove_node` method explicitly raises this error (defined in [`treelib/exceptions.py`](https://github.com/caesar0301/treelib/blob/main/treelib/exceptions.py)) when the identifier is not found, allowing you to handle missing nodes gracefully without pre-checking.

### Can I recover a deleted subtree?

Once you execute `remove_node`, the nodes are permanently deleted from the tree's internal storage. However, if you need to preserve the branch for later use, use `remove_subtree` instead. This method, located at lines 1629‑1650 in [`treelib/tree.py`](https://github.com/caesar0301/treelib/blob/main/treelib/tree.py), returns a new `Tree` instance containing the detached nodes, allowing you to re-insert them elsewhere or archive them before permanent deletion.

### Does `remove_node` update the tree structure automatically?

Yes, the method handles all structural updates internally. After removing the target nodes from the `_nodes` dictionary, it updates the parent node's forward pointer (`_fpointer`) to remove references to the deleted children. This ensures the tree remains consistent without requiring manual pointer management or re-balancing operations.