{"record":{"id":"0bc5e6c00c4ffe55","repo":"bevyengine/bevy","slug":"cannot-interpolate-between-two-values-of-different","errorCode":null,"errorMessage":"cannot interpolate between two values of different units","messagePattern":"cannot interpolate between two values of different units","errorType":"exception","errorClass":"MismatchedUnitsError","httpStatus":null,"severity":"warning","filePath":"crates/bevy_math/src/common_traits.rs","lineNumber":553,"sourceCode":"}\n\nall_tuples_enumerated!(\n    #[doc(fake_variadic)]\n    impl_stable_interpolate_tuple,\n    1,\n    11,\n    T\n);\n\nimpl<T: StableInterpolate, const LEN: usize> StableInterpolate for [T; LEN] {\n    fn interpolate_stable(&self, other: &Self, t: f32) -> Self {\n        core::array::from_fn(|i| self[i].interpolate_stable(&other[i], t))\n    }\n}\n\n/// Error produced when the values to be interpolated are not in the same units.\n#[derive(Clone, Debug, Error)]\n#[error(\"cannot interpolate between two values of different units\")]\npub struct MismatchedUnitsError;\n\n/// A trait that indicates that a value _may_ be interpolable via [`StableInterpolate`]. An\n/// interpolation may fail if the values have different units - for example, attempting to\n/// interpolate between [`Val::Px`] and [`Val::Percent`] will fail,\n/// even though they are the same Rust type.\n///\n/// Fallible interpolation can be used for animated transitions, which can be set up to fail\n/// gracefully if the values cannot be interpolated. For example, a transition could smoothly\n/// go from `Val::Px(10)` to `Val::Px(20)`, but if the user attempts to go from `Val::Px(10)` to\n/// `Val::Percent(10)`, the animation player can detect the failure and simply snap to the new\n/// value without interpolating.\n///\n/// An animation clip system can incorporate fallible interpolation to support a broad set of\n/// sequenced parameter values. This can include numeric types, which always interpolate,\n/// enum types, which may or may not interpolate depending on the units, and non-interpolable\n/// types, which always jump immediately to the new value without interpolation. This means, for\n/// example, that you can have an animation track whose value type is a boolean or a string.","sourceCodeStart":535,"sourceCodeEnd":571,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_math/src/common_traits.rs#L535-L571","documentation":"TryStableInterpolate for bevy_ui::Val returns MismatchedUnitsError when the two endpoints use different variants — e.g. Val::Px(10.0) to Val::Percent(10.0) (crates/bevy_ui/src/geometry.rs:531-551). Interpolation within one variant is well-defined; across variants it is not, so the fallible API reports it. The design intent is that animated transitions detect the failure and snap to the target instead of blending.","triggerScenarios":"Calling try_interpolate_stable (or an animation/tween system built on it) with endpoints of different Val variants: Px vs Percent, VMin vs Vw, etc.","commonSituations":"Style transitions authored with mixed units; UI defaults specified in Px while targets are in Percent; tween configs copied between UIs that use different unit systems.","solutions":["Use the same Val variant for both endpoints of the transition.","On Err(MismatchedUnitsError), snap to the target value — this is the documented graceful path.","Convert one endpoint into the other's unit using the resolved layout size before animating."],"exampleFix":"// before\nlet start = Val::Px(10.0);\nlet end = Val::Percent(50.0); // different variant -> MismatchedUnitsError\n\n// after: snap on mismatch\nlet value = match start.try_interpolate_stable(&end, t) {\n    Ok(v) => v,\n    Err(MismatchedUnitsError) => end,\n};","handlingStrategy":"fallback","validationCode":"use bevy_math::{MismatchedUnitsError, TryStableInterpolate};\nuse bevy_ui::Val;\n\nfn same_unit(a: &Val, b: &Val) -> bool {\n    matches!((a, b),\n        (Val::Px(_), Val::Px(_))\n        | (Val::Percent(_), Val::Percent(_))\n        | (Val::Vw(_), Val::Vw(_))\n        | (Val::Vh(_), Val::Vh(_))\n        | (Val::VMin(_), Val::VMin(_))\n        | (Val::VMax(_), Val::VMax(_))\n        | (Val::Auto, Val::Auto))\n}\n\nfn blend(a: Val, b: Val, t: f32) -> Val {\n    if same_unit(&a, &b) { a.try_interpolate_stable(&b, t).unwrap_or(b) } else { b }\n}","typeGuard":null,"tryCatchPattern":"let value = match start.try_interpolate_stable(&end, t) {\n    Ok(v) => v,\n    Err(MismatchedUnitsError) => end, // documented graceful snap\n};","preventionTips":["Author transitions with matching Val variants on both ends.","Treat interpolation failure as a snap, never as unwrap().","When mixing units, resolve both to Px using layout size before animating."],"tags":["bevy","math","interpolation","ui","style","units","animation"],"backgroundTag":"unit-mismatch-interpolation","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}