leptos-rs/leptos · error

internal error: entered unreachable code

Error message

internal error: entered unreachable code

What it means

The `rebuild()` for a reactive `inner_html` binding creates/replaces a `RenderEffect` whose closure expects `Some(prev)` — the previous HTML string state used by `value.rebuild(&mut state)` to diff DOM inner HTML. `unreachable!()` means the effect ran with `prev == None`: the effect was seeded without a value (`prev_value` was `None`) or the value had been taken, so the diffing code had nothing to rebuild from.

Source

Thrown at tachys/src/reactive_graph/inner_html.rs:65

            if let Some(mut state) = prev {
                value.rebuild(&mut state);
                state
            } else {
                value.build(&el)
            }
        })
    }

    fn rebuild(mut self, state: &mut Self::State) {
        let prev_value = state.take_value();
        *state = RenderEffect::new_with_value(
            move |prev| {
                let value = self.invoke();
                if let Some(mut state) = prev {
                    value.rebuild(&mut state);
                    state
                } else {
                    unreachable!()
                }
            },
            prev_value,
        );
    }

    fn into_cloneable(self) -> Self::Cloneable {
        self.into_shared()
    }

    fn into_cloneable_owned(self) -> Self::CloneableOwned {
        self.into_shared()
    }

    fn dry_resolve(&mut self) {
        self.invoke();
    }

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Defer signal updates until after hydration/mount of the element completes.
  2. Seed the effect with `RenderEffect::new_with_value(f, initial_html)` when constructing state manually.
  3. Ensure `build`/`hydrate` runs before any `rebuild` on the same state.
  4. Update leptos/tachys to the latest 0.7.x where async-effect seeding was corrected.

Example fix

// before
let prev_value: Option<String> = None; // or taken
*state = RenderEffect::new_with_value(move |prev| { /* expects Some */ }, prev_value);
// after
let prev_value = Some(current_html);
*state = RenderEffect::new_with_value(move |prev: Option<String>| { /* Some guaranteed */ }, prev_value);
Defensive patterns

Strategy: fallback

Validate before calling

// Only bind reactive inner_html after the element state exists
let mounted = RwSignal::new(false);
view! { <div inner_html={move || { if mounted.get() { html_signal.get() } else { String::new() } }} /> }
// set mounted.set(true) after mount

Type guard

fn has_prev_html(prev: &Option<String>) -> bool { prev.is_some() }

Try / catch

// Panic; capture with hook
std::panic::set_hook(Box::new(|info| {
    if info.to_string().contains("unreachable") {
        leptos::logging::error!("inner_html rebuild without prev state: {info}");
    }
}));

Prevention

When it happens

Trigger: Updating a signal driving `inner_html={...}` when the binding's effect previous value is `None` — typically after the state was built via a non-seeding path (`RenderEffect::new` in hydration) or `rebuild` called before `build` committed `prev_value`.

Common situations: Hydrating SSR content where the reactive `inner_html` fires before the hydration path stores its initial value; custom view code calling `rebuild` on a state constructed manually; leptos version mismatches between the view macro and tachys runtime.

Related errors


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