iced-rs/iced · error

Downcast widget state

Error message

Downcast widget state

What it means

The state exists (State::Some(Box<dyn Any>)) but Any::downcast_ref::<T>() returned None: the concrete type inside the box is not T. The expect("Downcast widget state") fires, meaning a plain type mismatch between the state a widget stored via State::new and the type its consumer requested via downcast_ref.

Solutions

  1. Make T exactly the type the widget creates with State::new for that tree row
  2. Re-check the tree traversal: the child index used for the downcast must be the child that owns the state
  3. If you changed the state type of a custom widget, force state recreation (fresh Tree, or a widget change that makes diff rebuild the row)
  4. Never rely on concrete internal state types of built-in iced widgets across versions; pin the iced version or use public APIs instead

Example fix

// before
let state = tree.state.downcast_ref::<OldState>(); // widget now stores NewState

// after - match the type the widget creates
impl<Message, Renderer> Widget<Message, Renderer> for MyWidget {
    fn state(&self) -> Option<tree::State> {
        Some(tree::State::new(NewState::default()))
    }
}
let state = tree.state.downcast_ref::<NewState>();
Defensive patterns

Strategy: type-guard

Validate before calling

// correlate the tree row with the state type before downcasting
if tree.tag != tree::Tag::of::<MyState>() {
    // wrong row or the widget stores a different type: fix traversal first
}

Type guard

fn as_state<T: 'static>(state: &tree::State) -> Option<&T> {
    match state {
        tree::State::Some(inner) => inner.downcast_ref::<T>(),
        tree::State::None => None,
    }
}

Prevention

When it happens

Trigger: Calling downcast_ref::<T>() with a T different from the type the widget passed to State::new; a custom widget whose state type changed between builds while an old Tree is reused; downcasting the state row of a different widget (wrong child index) that stores another type; downcasting built-in widget internals whose state types are private and version-specific.

Common situations: Refactoring a custom widget's state struct without regenerating the tree; copy-pasted downcast_ref calls from another widget; relying on internal state types of TextInput/Scrollable-style widgets that changed across iced releases.

Related errors


AI-assisted analysis of iced-rs/iced@f8127c84dd (2026-08-17). Data as JSON: /api/errors/926f0f2dece81260. Report an issue: GitHub.

Appendix: source

Thrown at core/src/widget/tree.rs:256

    /// 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 f8127c84dd)