{"record":{"id":"0498cd928e3eb63e","repo":"leptos-rs/leptos","slug":"children-should-not-be-removed-until-we-render-her","errorCode":null,"errorMessage":"children should not be removed until we render here","messagePattern":"children should not be removed until we render here","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"leptos/src/suspense_component.rs","lineNumber":463,"sourceCode":"                // that it will wait for those to resolve\n                select! {\n                    // if there are local resources, bail\n                    // this will only have fired by this point for local resources accessed\n                    // *synchronously*\n                    _ = local_rx => {\n                        let sc = Owner::current_shared_context().expect(\"no shared context\");\n                        sc.set_incomplete_chunk(self.id);\n                        if let Some(tx) =\n                            notify_error_boundary.write_value().take()\n                        {\n                            let _ = tx.send(());\n                        }\n                        None\n                    }\n                    _ = tasks_rx => {\n                        let children = {\n                            let mut children_lock = children.lock().or_poisoned();\n                            children_lock.take().expect(\"children should not be removed until we render here\")\n                        };\n\n                        // if we ran this earlier, reactive reads would always be registered as None\n                        // this is fine in the case where we want to use Suspend and .await on some future\n                        // but in situations like a <For each=|| some_resource.snapshot()/> we actually\n                        // want to be able to 1) synchronously read a resource's value, but still 2) wait\n                        // for it to load before we render everything\n                        let mut children = Box::pin(children.resolve().fuse());\n\n                        // we continue racing the children against the \"do we have any local\n                        // resources?\" Future\n                        select! {\n                            _ = local_rx => {\n                                let sc = Owner::current_shared_context().expect(\"no shared context\");\n                                sc.set_incomplete_chunk(self.id);\n                                if let Some(tx) =\n                                    notify_error_boundary.write_value().take()\n                                {","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/leptos-rs/leptos/blob/32d20f6c9d517451d77beb68d88c7716823dac41/leptos/src/suspense_component.rs#L445-L481","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Serialize each Suspense view exactly once per request; build a fresh view per render pass.","Don't reuse view/component instances across requests — construct them inside the per-request render closure.","If a retry of a failed stream is needed, recreate the whole view tree rather than re-calling to_html on the same instance.","Update leptos if this reproduces on a plain single-render path — it indicates a framework lifecycle bug worth reporting."],"exampleFix":"// before\nstatic VIEW: ...; // shared Suspense instance reused across requests\n// after\nlet html = render_to_string(|cx| view! { cx, <Suspense>.../* fresh per request */ });\n","handlingStrategy":"validation","validationCode":"// ensure each Suspense instance is serialized exactly once per request\nlet view = view! { cx, <Suspense fallback=...>... }; // build fresh per request\nlet html = render_to_string(move || view.clone()).await; // don't reuse a consumed instance\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Construct views per request; never share Suspense instances across renders","Serialize each response exactly once (no retry on the same instance)","If a stream fails, rebuild the whole view tree before retrying","Update leptos to the latest patch — lifecycle double-take bugs are fixed upstream"],"tags":["leptos","ssr","suspense","lifecycle"],"backgroundTag":"double-render-state-consumed","analyzedSha":"32d20f6c9d517451d77beb68d88c7716823dac41","analyzedAt":"2026-09-01T18:55:42.580Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}