{"record":{"id":"fdd60a955f6d7ddc","repo":"3b1b/manim","slug":"animation-only-works-for-mobjects","errorCode":null,"errorMessage":"Animation only works for Mobjects.","messagePattern":"Animation only works for Mobjects\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"manimlib/animation/animation.py","lineNumber":58,"sourceCode":"        # If set to True, the mobject itself will have its internal updaters called,\n        # but the start or target mobjects would not be suspended. To completely suspend\n        # updating, call mobject.suspend_updating() before the animation\n        suspend_mobject_updating: bool = False,\n    ):\n        self._validate_input_type(mobject)\n        self.mobject = mobject\n        self.run_time = run_time\n        self.time_span = time_span\n        self.rate_func = rate_func\n        self.name = name or self.__class__.__name__ + str(self.mobject)\n        self.remover = remover\n        self.final_alpha_value = final_alpha_value\n        self.lag_ratio = lag_ratio\n        self.suspend_mobject_updating = suspend_mobject_updating\n\n    def _validate_input_type(self, mobject: Mobject) -> None:\n        if not isinstance(mobject, Mobject):\n            raise TypeError(\"Animation only works for Mobjects.\")\n\n    def __str__(self) -> str:\n        return self.name\n\n    def begin(self) -> None:\n        # This is called right as an animation is being\n        # played.  As much initialization as possible,\n        # especially any mobject copying, should live in\n        # this method\n        if self.time_span is not None:\n            start, end = self.time_span\n            self.run_time = max(end, self.run_time)\n        self.mobject.set_animating_status(True)\n        self.starting_mobject = self.create_starting_mobject()\n        if self.suspend_mobject_updating:\n            self.mobject_was_updating = not self.mobject.updating_suspended\n            self.mobject.suspend_updating()\n        self.families = list(self.get_all_families_zipped())","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/3b1b/manim/blob/dee01804d47b9f94402d71472674710dcac125b8/manimlib/animation/animation.py#L40-L76","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the content in the appropriate mobject: Text('hello'), MathTex, DecimalNumber, etc.","If animating a changing number, use a ValueTracker with always_redraw, not the raw float","In custom Animation subclasses, validate/convert before calling super().__init__"],"exampleFix":"# before\nself.play(Animation(\"text\"))\n# after\nself.play(Write(Text(\"text\")))","handlingStrategy":"type-guard","validationCode":"assert isinstance(obj, Mobject), f\"Animation needs a Mobject, got {type(obj).__name__}\"","typeGuard":"def is_mobject(x) -> bool:\n    return isinstance(x, Mobject)","tryCatchPattern":null,"preventionTips":["Wrap raw data in Text/MathTex/DecimalNumber/ValueTracker before animating","Type-hint your scene helpers as (mobject: Mobject, ...) so mypy catches bad calls statically"],"tags":["animation","type-error","mobject","type-guard"],"backgroundTag":null,"analyzedSha":"dee01804d47b9f94402d71472674710dcac125b8","analyzedAt":"2026-08-14T19:53:44.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}