3b1b/manim · error · TypeError

Animation only works for Mobjects.

Error message

Animation only works for Mobjects.

What it means

Animation._validate_input_type enforces that every animation operates on a Mobject instance; anything else (string, numpy array, list, arbitrary object) raises TypeError at construction time. This is the library's explicit type boundary for the Animation API.

Source

Thrown at manimlib/animation/animation.py:58

        # If set to True, the mobject itself will have its internal updaters called,
        # but the start or target mobjects would not be suspended. To completely suspend
        # updating, call mobject.suspend_updating() before the animation
        suspend_mobject_updating: bool = False,
    ):
        self._validate_input_type(mobject)
        self.mobject = mobject
        self.run_time = run_time
        self.time_span = time_span
        self.rate_func = rate_func
        self.name = name or self.__class__.__name__ + str(self.mobject)
        self.remover = remover
        self.final_alpha_value = final_alpha_value
        self.lag_ratio = lag_ratio
        self.suspend_mobject_updating = suspend_mobject_updating

    def _validate_input_type(self, mobject: Mobject) -> None:
        if not isinstance(mobject, Mobject):
            raise TypeError("Animation only works for Mobjects.")

    def __str__(self) -> str:
        return self.name

    def begin(self) -> None:
        # This is called right as an animation is being
        # played.  As much initialization as possible,
        # especially any mobject copying, should live in
        # this method
        if self.time_span is not None:
            start, end = self.time_span
            self.run_time = max(end, self.run_time)
        self.mobject.set_animating_status(True)
        self.starting_mobject = self.create_starting_mobject()
        if self.suspend_mobject_updating:
            self.mobject_was_updating = not self.mobject.updating_suspended
            self.mobject.suspend_updating()
        self.families = list(self.get_all_families_zipped())

View on GitHub (pinned to dee01804d4)

Solutions

  1. Wrap the content in the appropriate mobject: Text('hello'), MathTex, DecimalNumber, etc.
  2. If animating a changing number, use a ValueTracker with always_redraw, not the raw float
  3. In custom Animation subclasses, validate/convert before calling super().__init__

Example fix

# before
self.play(Animation("text"))
# after
self.play(Write(Text("text")))
Defensive patterns

Strategy: type-guard

Validate before calling

assert isinstance(obj, Mobject), f"Animation needs a Mobject, got {type(obj).__name__}"

Type guard

def is_mobject(x) -> bool:
    return isinstance(x, Mobject)

Prevention

When it happens

Trigger: Animation('hello') or a custom animation subclass whose __init__ forwards a non-Mobject first argument; passing a Mobject-type name imported from the wrong module (e.g. a stub or mock) that fails isinstance.

Common situations: Wrapping raw data (strings, arrays) instead of mobjects like Text/MathTex/ValueTracker; test code passing mocks or duck-typed stand-ins; subclassing Animation and forgetting to convert the input to a Mobject first.

Related errors


AI-assisted analysis of 3b1b/manim@dee01804d4 (2026-08-14). Data as JSON: /api/errors/fdd60a955f6d7ddc. Report an issue: GitHub.