Textualize/textual · error · AnimationError
Don't know how to animate {value!r}; Can only animate <int>,
Error message
Don't know how to animate {value!r}; Can only animate <int>, <float>, or objects with a blend method What it means
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.
Source
Thrown at src/textual/_animator.py:418
if hasattr(obj, "__textual_animation__"):
animation = getattr(obj, "__textual_animation__")(
attribute,
getattr(obj, attribute),
value,
start_time,
duration=duration,
speed=speed,
easing=easing_function,
on_complete=on_complete,
level=level,
)
if animation is None:
if not isinstance(value, (int, float)) and not isinstance(
value, Animatable
):
raise AnimationError(
f"Don't know how to animate {value!r}; "
"Can only animate <int>, <float>, or objects with a blend method"
)
start_value = getattr(obj, attribute)
if start_value == value:
self._animations.pop(animation_key, None)
if on_complete is not None:
self.app.call_later(on_complete)
return
if duration is not None:
animation_duration = duration
else:
if hasattr(value, "get_distance_to"):
animation_duration = value.get_distance_to(start_value) / (
speed or 50
)View on GitHub (pinned to 06dbeef4bb)
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
Example fix
# before
widget.animate("offset", (10, 20)) # tuple not animatable
# after
from textual.geometry import Offset
widget.animate("offset", Offset(10, 20)) Defensive patterns
Strategy: type-guard
Validate before calling
from textual._animator import Animatable
if not isinstance(value, (int, float)) and not hasattr(value, 'blend'):
value = float(value) # or convert to an Animatable type Type guard
from textual._animator import Animatable
def is_blendable(value: object) -> bool:
return isinstance(value, (int, float, Animatable)) or callable(getattr(value, 'blend', None)) Try / catch
from textual._animator import AnimationError
try:
widget.animate(attr, value)
except AnimationError:
setattr(widget, attr, value) # jump-cut fallback Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Can't animate attribute {attribute!r} on {obj!r}; attribute
- Can't encode {datum!r}
- must be bytes
- {duration!r} is not a valid duration.
- More data expected
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/0ca9d94260835054.
Report an issue: GitHub.