slint-ui/slint · error
binding was of the wrong type
Error message
binding was of the wrong type
What it means
In the non-animated `Rtti::set_binding` implementation, the user-supplied binding is evaluated and its `Value` result is converted with `try_into()` to the property's native type; a failed conversion is mapped to `Err(())` and then immediately unwrapped with `.expect("binding was of the wrong type")`. This panics when a binding set through the RTTI/dynamic-property API produces a Value that cannot be converted into the property element type T.
Solutions
- Make the binding closure return a `Value` whose type matches the property's declared type (use the property's default value as a template)
- Check the property's type via RTTI (`PropertyRtti::type_()`) before binding and construct the matching Value variant
- If types can vary at runtime, perform an explicit, graceful conversion inside the closure instead of relying on try_into
Example fix
// before
handle.set_binding(prop, || Value::String("42".into()), AnimatedBindingKind::NotAnimated);
// after
handle.set_binding(prop, || Value::Number(42.0), AnimatedBindingKind::NotAnimated); Defensive patterns
Strategy: type-guard
Validate before calling
// check the property's declared type before binding let prop_type = prop_rtti.ty(); let value_type = std::mem::discriminant(&value); assert_eq!(value_type_matches(prop_type, &value), true);
Type guard
fn value_matches(prop_ty: ValueType, v: &Value) -> bool {
matches!((prop_ty, v),
(ValueType::Number, Value::Number) |
(ValueType::String, Value::String) |
(ValueType::Bool, Value::Bool) | /* ... */ )
} Try / catch
// expect() panics, so use catch_unwind only as a last resort let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| handle.set_binding(prop, binding, kind)));
Prevention
- Query PropertyRtti::type_() and construct the matching Value variant
- Derive binding values from the property's default value
- Keep FFI/interpreter property setters in sync with .slint declarations
- Add unit tests that set every dynamic property once
When it happens
Trigger: Calling `set_binding` on a dynamically-accessed property (e.g. via the interpreter / `DynamicType` RTTI path) where the closure returns a `Value` of a different type than the property declared (e.g. assigning a string Value to an int property, or a length to a color).
Common situations: Interpreter/Node/Python/C++ FFI code setting properties with values of the wrong Slint type; property type changed in .slint markup without updating the binding code.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- a setter belongs to a field
- an identifier
- Cannot get property
- Cannot invoke callback
- Cannot set callback
AI-assisted analysis of slint-ui/slint@3a7e700487 (2026-09-16).
Data as JSON: /api/errors/70ec7f4c31c0e728.
Report an issue: GitHub.
Appendix: source
Thrown at internal/core/rtti.rs:260
) -> Result<(), ()> {
if animation.is_some() {
Err(())
} else {
self.apply_pin(item).set(value.try_into().map_err(|_| ())?);
Ok(())
}
}
fn set_binding(
&self,
item: Pin<&Item>,
binding: Box<dyn Fn() -> Value>,
animation: AnimatedBindingKind,
) -> Result<(), ()> {
if !matches!(animation, AnimatedBindingKind::NotAnimated) {
Err(())
} else {
self.apply_pin(item).set_binding(move || {
binding().try_into().map_err(|_| ()).expect("binding was of the wrong type")
});
Ok(())
}
}
fn offset(&self) -> usize {
self.get_byte_offset()
}
#[cfg(slint_debug_property)]
fn set_debug_name(&self, item: Pin<&Item>, name: alloc::string::String) {
self.apply_pin(item).debug_name.replace(name);
}
#[allow(unsafe_code)]
unsafe fn link_two_ways(&self, item: Pin<&Item>, property2: *const c_void) {
let p1 = self.apply_pin(item);
// Safety: that's the invariant of this function
let p2 = unsafe { Pin::new_unchecked((property2 as *const Property<T>).as_ref().unwrap()) };View on GitHub (pinned to 3a7e700487)