leptos-rs/leptos · error

children should not be removed until we render here

Error message

children should not be removed until we render here

What it means

In the same SSR Suspense streaming path, after all tasks resolve leptos takes the buffered children out of a Mutex and expects them present: children_lock.take().expect(...). The panic means the children slot was already taken (double render of the same Suspense) or never set before tasks_rx fired — an invariant break in the render lifecycle.

Source

Thrown at leptos/src/suspense_component.rs:463

                // that it will wait for those to resolve
                select! {
                    // if there are local resources, bail
                    // this will only have fired by this point for local resources accessed
                    // *synchronously*
                    _ = local_rx => {
                        let sc = Owner::current_shared_context().expect("no shared context");
                        sc.set_incomplete_chunk(self.id);
                        if let Some(tx) =
                            notify_error_boundary.write_value().take()
                        {
                            let _ = tx.send(());
                        }
                        None
                    }
                    _ = tasks_rx => {
                        let children = {
                            let mut children_lock = children.lock().or_poisoned();
                            children_lock.take().expect("children should not be removed until we render here")
                        };

                        // if we ran this earlier, reactive reads would always be registered as None
                        // this is fine in the case where we want to use Suspend and .await on some future
                        // but in situations like a <For each=|| some_resource.snapshot()/> we actually
                        // want to be able to 1) synchronously read a resource's value, but still 2) wait
                        // for it to load before we render everything
                        let mut children = Box::pin(children.resolve().fuse());

                        // we continue racing the children against the "do we have any local
                        // resources?" Future
                        select! {
                            _ = local_rx => {
                                let sc = Owner::current_shared_context().expect("no shared context");
                                sc.set_incomplete_chunk(self.id);
                                if let Some(tx) =
                                    notify_error_boundary.write_value().take()
                                {

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Serialize each Suspense view exactly once per request; build a fresh view per render pass.
  2. Don't reuse view/component instances across requests — construct them inside the per-request render closure.
  3. If a retry of a failed stream is needed, recreate the whole view tree rather than re-calling to_html on the same instance.
  4. Update leptos if this reproduces on a plain single-render path — it indicates a framework lifecycle bug worth reporting.

Example fix

// before
static VIEW: ...; // shared Suspense instance reused across requests
// after
let html = render_to_string(|cx| view! { cx, <Suspense>.../* fresh per request */ });
Defensive patterns

Strategy: validation

Validate before calling

// ensure each Suspense instance is serialized exactly once per request
let view = view! { cx, <Suspense fallback=...>... }; // build fresh per request
let html = render_to_string(move || view.clone()).await; // don't reuse a consumed instance

Prevention

When it happens

Trigger: Rendering the same Suspense view into HTML twice (calling the serializer twice on one instance); children lock poisoned-adjacent misuse where a previous take consumed the value; custom code re-entering to_html_async_with_buf for the same component.

Common situations: Custom server middleware invoking HTML serialization twice for one request (e.g. fallback + retry path); reusing a view builder across requests; a library bug after partial streaming failure and retry of the same chunk.

Related errors


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