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

  1. Animate numeric attributes (int/float) instead of strings or tuples
  2. Use Textual's Animatable types such as Offset for positions
  3. Implement a blend(self, target, factor) method (or subclass Animatable) on your custom class so the animator can interpolate it
  4. 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

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


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/0ca9d94260835054. Report an issue: GitHub.