leptos-rs/leptos · error

tried to remove a parentless node

Error message

tried to remove a parentless node

What it means

MockDom::remove calls Self::get_parent(node) and unwraps it with expect before delegating to remove_node. This panic means the node has no parent in the mock DOM — it is detached (a root node, already removed, or never mounted). The mock DOM remove API assumes you only remove attached children.

Source

Thrown at tachys/src/renderer/mock_dom.rs:535

                    .iter()
                    .position(|item| item.0 == child.0)
                    .expect("anchor is not a child of the parent");
                Some(children.remove(current_pos))
            } else {
                None
            }
        })
        .flatten()?;
        Document::with_node_mut(child.0, |node| {
            node.parent = None;
        });
        Some(child)
    }

    fn remove(node: &Self::Node) {
        let parent = Element(Node(
            Self::get_parent(node)
                .expect("tried to remove a parentless node")
                .0,
        ));
        Self::remove_node(&parent, node);
    }

    fn get_parent(node: &Self::Node) -> Option<Self::Node> {
        Document::with_node(node.0, |node| node.parent)
            .flatten()
            .map(Node)
    }

    fn first_child(node: &Self::Node) -> Option<Self::Node> {
        Document::with_node(node.0, |node| match &node.ty {
            NodeType::Text(_) => None,
            NodeType::Element { children, .. } => children.first().cloned(),
            NodeType::Placeholder => None,
        })
        .flatten()

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Check MockDom::get_parent(node).is_some() before calling remove.
  2. Make view unmount idempotent: track mounted state and skip remove if already detached.
  3. For root containers, remove the node from its holder rather than calling remove on a parentless node.

Example fix

// before
MockDom::remove(&node);
// after
if MockDom::get_parent(&node).is_some() {
    MockDom::remove(&node);
}
Defensive patterns

Strategy: validation

Validate before calling

if MockDom::get_parent(&node).is_some() {
    MockDom::remove(&node);
}

Type guard

fn has_parent(node: &MockDomNode) -> bool {
    MockDom::get_parent(node).is_some()
}

Try / catch

let ok = std::panic::catch_unwind(|| MockDom::remove(&node));
if ok.is_err() { /* node was detached; skip cleanup */ }

Prevention

When it happens

Trigger: Calling Renderer::remove / MockDom::remove on a node that was never inserted into the mock tree, was already removed, or is a top-level node without a parent element.

Common situations: Unmounting a view twice; removing a node whose parent element was dropped/replaced earlier; test code cleaning up a manually created root node; conditional views whose cleanup runs after the branch node was already discarded.

Related errors


AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01). Data as JSON: /api/errors/bd4106fd105e3b05. Report an issue: GitHub.