{"record":{"id":"bd4106fd105e3b05","repo":"leptos-rs/leptos","slug":"tried-to-remove-a-parentless-node","errorCode":null,"errorMessage":"tried to remove a parentless node","messagePattern":"tried to remove a parentless node","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tachys/src/renderer/mock_dom.rs","lineNumber":535,"sourceCode":"                    .iter()\n                    .position(|item| item.0 == child.0)\n                    .expect(\"anchor is not a child of the parent\");\n                Some(children.remove(current_pos))\n            } else {\n                None\n            }\n        })\n        .flatten()?;\n        Document::with_node_mut(child.0, |node| {\n            node.parent = None;\n        });\n        Some(child)\n    }\n\n    fn remove(node: &Self::Node) {\n        let parent = Element(Node(\n            Self::get_parent(node)\n                .expect(\"tried to remove a parentless node\")\n                .0,\n        ));\n        Self::remove_node(&parent, node);\n    }\n\n    fn get_parent(node: &Self::Node) -> Option<Self::Node> {\n        Document::with_node(node.0, |node| node.parent)\n            .flatten()\n            .map(Node)\n    }\n\n    fn first_child(node: &Self::Node) -> Option<Self::Node> {\n        Document::with_node(node.0, |node| match &node.ty {\n            NodeType::Text(_) => None,\n            NodeType::Element { children, .. } => children.first().cloned(),\n            NodeType::Placeholder => None,\n        })\n        .flatten()","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/leptos-rs/leptos/blob/32d20f6c9d517451d77beb68d88c7716823dac41/tachys/src/renderer/mock_dom.rs#L517-L553","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check MockDom::get_parent(node).is_some() before calling remove.","Make view unmount idempotent: track mounted state and skip remove if already detached.","For root containers, remove the node from its holder rather than calling remove on a parentless node."],"exampleFix":"// before\nMockDom::remove(&node);\n// after\nif MockDom::get_parent(&node).is_some() {\n    MockDom::remove(&node);\n}","handlingStrategy":"validation","validationCode":"if MockDom::get_parent(&node).is_some() {\n    MockDom::remove(&node);\n}","typeGuard":"fn has_parent(node: &MockDomNode) -> bool {\n    MockDom::get_parent(node).is_some()\n}","tryCatchPattern":"let ok = std::panic::catch_unwind(|| MockDom::remove(&node));\nif ok.is_err() { /* node was detached; skip cleanup */ }","preventionTips":["Check get_parent before every remove.","Track mounted/detached state in your view wrappers.","Do not call remove on root container nodes.","Guard cleanup callbacks so they fire at most once."],"tags":["dom","mock-dom","panic","renderer","detached-node"],"backgroundTag":"parentless-node-remove","analyzedSha":"32d20f6c9d517451d77beb68d88c7716823dac41","analyzedAt":"2026-09-01T18:55:42.580Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}