DioxusLabs/dioxus · error
The tree has not been built yet. Make sure to call rebuild o
Error message
The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.
What it means
ScopeState::root_node() returns the element a scope last rendered, but a freshly constructed VirtualDom has not run a render pass yet, so last_rendered_node is None and the expect panics. The method's docs explicitly state it panics unless the tree has been built. This is a lifecycle error: build the tree before traversing it.
Source
Thrown at packages/core/src/scopes.rs:91
pub struct ScopeState {
pub(crate) runtime: Rc<Runtime>,
pub(crate) context_id: ScopeId,
/// The last node that has been rendered for this component. This node may not ben mounted
/// During suspense, this component can be rendered in the background multiple times
pub(crate) last_rendered_node: Option<LastRenderedNode>,
pub(crate) props: BoxedAnyProps,
pub(crate) reactive_context: ReactiveContext,
}
impl ScopeState {
/// Get a handle to the currently active head node arena for this Scope
///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
///
/// Panics if the tree has not been built yet.
pub fn root_node(&self) -> &VNode {
self.try_root_node()
.expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
}
/// Try to get a handle to the currently active head node arena for this Scope
///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
///
/// Returns [`None`] if the tree has not been built yet.
pub fn try_root_node(&self) -> Option<&VNode> {
match &self.last_rendered_node {
Some(LastRenderedNode::Real(vnode)) => Some(vnode),
Some(LastRenderedNode::Placeholder(vnode, _)) => Some(vnode),
None => None,
}
}
/// Returns the scope id of this [`ScopeState`].
pub fn id(&self) -> ScopeId {
self.context_idView on GitHub (pinned to 393d190a80)
Solutions
- Call vdom.rebuild_in_place() (or run the rebuild flow in your renderer) before touching scopes
- Use scope.try_root_node() and skip scopes that return None instead of root_node()
- For suspended scopes, wait until they resolve before traversal
Example fix
// before let vdom = VirtualDom::new(App); let node = vdom.scopes()[0].root_node(); // panics // after let mut vdom = VirtualDom::new(App); vdom.rebuild_in_place(); let node = vdom.scopes()[0].root_node();
Defensive patterns
Strategy: validation
Validate before calling
// Guard traversal with the non-panicking variant:
for scope in vdom.scopes() {
if let Some(root) = scope.try_root_node() {
visit(root);
} // skip scopes not built yet (e.g. suspended)
} Type guard
fn scope_is_built(scope: &dioxus_core::ScopeState) -> bool {
scope.try_root_node().is_some()
} Prevention
- Always run rebuild_in_place() (or your renderer's rebuild flow) right after VirtualDom::new before traversal
- Prefer try_root_node() in generic/renderer code; reserve root_node() for contexts you know are post-render
- In tests, make rebuild part of the standard vdom construction helper
When it happens
Trigger: Calling scope.root_node() (custom renderers, SSR walkers, tests, html rendering APIs) on a VirtualDom created with VirtualDom::new but never passed through rebuild()/rebuild_in_place(); accessing root_node before the first render commit.
Common situations: Writing a custom renderer or SSR integration and iterating vdom.scopes() before rebuild; unit tests that construct a VirtualDom and immediately inspect its nodes.
Related errors
- The hook list is already borrowed: This error is likely caus
- Callback was called after the runtime was dropped
- should have access to the Window
- todo: convert_resize_data in dioxus-native. requires support
- todo: convert_visible_data in dioxus-native. requires suppor
AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16).
Data as JSON: /api/errors/d300662f31b168bb.
Report an issue: GitHub.