{"record":{"id":"0ca9d94260835054","repo":"Textualize/textual","slug":"don-t-know-how-to-animate-value-r-can-only-anim","errorCode":null,"errorMessage":"Don't know how to animate {value!r}; Can only animate <int>, <float>, or objects with a blend method","messagePattern":"Don't know how to animate (.+?); Can only animate <int>, <float>, or objects with a blend method","errorType":"exception","errorClass":"AnimationError","httpStatus":null,"severity":"error","filePath":"src/textual/_animator.py","lineNumber":418,"sourceCode":"\n        if hasattr(obj, \"__textual_animation__\"):\n            animation = getattr(obj, \"__textual_animation__\")(\n                attribute,\n                getattr(obj, attribute),\n                value,\n                start_time,\n                duration=duration,\n                speed=speed,\n                easing=easing_function,\n                on_complete=on_complete,\n                level=level,\n            )\n\n        if animation is None:\n            if not isinstance(value, (int, float)) and not isinstance(\n                value, Animatable\n            ):\n                raise AnimationError(\n                    f\"Don't know how to animate {value!r}; \"\n                    \"Can only animate <int>, <float>, or objects with a blend method\"\n                )\n\n            start_value = getattr(obj, attribute)\n            if start_value == value:\n                self._animations.pop(animation_key, None)\n                if on_complete is not None:\n                    self.app.call_later(on_complete)\n                return\n\n            if duration is not None:\n                animation_duration = duration\n            else:\n                if hasattr(value, \"get_distance_to\"):\n                    animation_duration = value.get_distance_to(start_value) / (\n                        speed or 50\n                    )","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/_animator.py#L400-L436","documentation":"Textual's animation engine can only interpolate values it knows how to blend: ints, floats, or objects implementing the Animatable protocol (a blend method). Any other value type raises AnimationError because no interpolation strategy exists for it.","triggerScenarios":"Calling animate() with a non-numeric, non-Animatable target value, e.g. widget.animate('label', 'hello'), animating a string, a tuple like (1, 2), or a plain dataclass without a blend method.","commonSituations":"Trying to fade text by animating a string attribute, animating positions as tuples instead of Offset (which is Animatable), or animating an enum/custom class that doesn't subclass Animatable.","solutions":["Animate numeric attributes (int/float) instead of strings or tuples","Use Textual's Animatable types such as Offset for positions","Implement a blend(self, target, factor) method (or subclass Animatable) on your custom class so the animator can interpolate it","Reconsider the approach: use timers or reactive watchers for non-animatable values"],"exampleFix":"# before\nwidget.animate(\"offset\", (10, 20))  # tuple not animatable\n\n# after\nfrom textual.geometry import Offset\nwidget.animate(\"offset\", Offset(10, 20))","handlingStrategy":"type-guard","validationCode":"from textual._animator import Animatable\nif not isinstance(value, (int, float)) and not hasattr(value, 'blend'):\n    value = float(value)  # or convert to an Animatable type","typeGuard":"from textual._animator import Animatable\n\ndef is_blendable(value: object) -> bool:\n    return isinstance(value, (int, float, Animatable)) or callable(getattr(value, 'blend', None))","tryCatchPattern":"from textual._animator import AnimationError\ntry:\n    widget.animate(attr, value)\nexcept AnimationError:\n    setattr(widget, attr, value)  # jump-cut fallback","preventionTips":["Only animate numbers or Animatable types like Offset","Wrap positions in Offset(...) rather than tuples","Implement blend() on custom value classes you want to animate"],"tags":["python","textual","animation","type-mismatch"],"backgroundTag":"unsupported-animation-type","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}