iced-rs/iced · error
Downcast on stateless state
Error message
Downcast on stateless state
What it means
`State::downcast_ref<T>` panics when the widget's `State` is `State::None`, i.e. the widget holds no state at all. The library treats downcasting a stateless state as a programming error rather than a recoverable condition, because the caller has already assumed a concrete state type exists. In iced, `State::None` is used for widgets with no state or as a placeholder after state removal.
Source
Thrown at core/src/widget/tree.rs:255
impl State {
/// Creates a new [`State`].
pub fn new<T>(state: T) -> Self
where
T: 'static,
{
State::Some(Box::new(state))
}
/// Downcasts the [`State`] to `T` and returns a reference to it.
///
/// # Panics
/// This method will panic if the downcast fails or the [`State`] is [`State::None`].
pub fn downcast_ref<T>(&self) -> &T
where
T: 'static,
{
match self {
State::None => panic!("Downcast on stateless state"),
State::Some(state) => state.downcast_ref().expect("Downcast widget state"),
}
}
/// Downcasts the [`State`] to `T` and returns a mutable reference to it.
///
/// # Panics
/// This method will panic if the downcast fails or the [`State`] is [`State::None`].
pub fn downcast_mut<T>(&mut self) -> &mut T
where
T: 'static,
{
match self {
State::None => panic!("Downcast on stateless state"),
State::Some(state) => state.downcast_mut().expect("Downcast widget state"),
}
}
}View on GitHub (pinned to d146509d89)
Solutions
- Check the state kind before downcasting: match on the returned `State` and only call `downcast_ref` for `State::Some`.
- Ensure state is initialized (e.g. via `State::new(...)` in the widget's `update`/`Widget::state`) before any downcast.
- Verify the widget id is valid and the widget still exists in the tree at the time of access.
- If a downcast can legitimately fail, use the internal `state.downcast_ref()` on the `State::Some` payload (returns `Option`/`Result`) instead of panicking.
Example fix
// before
let s = tree.get_state(id).downcast_ref::<MyState>();
// after
let s = match tree.get_state(id) {
widget::tree::State::Some(_) => tree.get_state(id).downcast_ref::<MyState>(),
widget::tree::State::None => return, // state not initialized yet
}; Defensive patterns
Strategy: type-guard
Validate before calling
let state = tree.get_state(id);
let is_ready = matches!(state, widget::tree::State::Some(_));
if !is_ready { return; } Type guard
fn has_state(state: &widget::tree::State) -> bool {
matches!(state, widget::tree::State::Some(_))
} Try / catch
// Rust panics are not catchable by Result; use std::panic::catch_unwind only at boundaries
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
tree.get_state(id).downcast_ref::<MyState>()
})); Prevention
- Always initialize widget state before first downcast (check `is_some` in `update`).
- Never cache widget ids across tree rebuilds without validating them.
- Prefer matching on `State` over blind downcasting in custom widgets.
- Add debug assertions on id validity in widget code.
When it happens
Trigger: Calling `tree.get_state(widget_id).downcast_ref::<MyState>()` on a widget whose state was never created, was reset to `State::None` (e.g. via `tree.set_state(State::None)` or widget diffing/removal), or whose id points at a stale/removed widget entry.
Common situations: Accessing widget state before the first `view` pass populated it; ids invalidated after widget tree rebuilds; custom widgets that lazily create state but are queried earlier; reusing a widget id across two tree positions.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Downcast widget state
- Caches must not be empty
- {error:?}
- Node configuration and count do not match
- Node configuration and split count do not match
AI-assisted analysis of iced-rs/iced@d146509d89 (2026-09-11).
Data as JSON: /api/errors/d7d2f6fb6d119d25.
Report an issue: GitHub.