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
- Make T exactly the type the widget creates with State::new for that tree row
- Re-check the tree traversal: the child index used for the downcast must be the child that owns the state
- 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)
- 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
- Always pair State::new(T) and downcast_ref::<T> in the same module so they change together
- Regenerate the widget tree after changing a custom widget's state type
- Pin the iced version if you must depend on internal widget state types
- Use tree.tag / Tag::of::<T>() to assert row ownership in debug builds
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
- Caches must not be empty
- Downcast on stateless state
- Create color
- Create softbuffer context
- Editor cannot have multiple strong references
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)