3b1b/manim · error · NotImplementedError

Method chaining is currently not supported for overridden an

Error message

Method chaining is currently not supported for overridden animations

What it means

NotImplementedError from _AnimBuilder.__getattr__ (manimlib/mobject/mobject.py:2157). The animate syntax builds a chain of method calls; some methods (e.g. certain VMobject/geometry methods) carry an _override_animate marker that swaps in a custom animation. Chaining further calls while such an overridden animation is already pending (or chaining after one within the same builder) is not supported by the builder's implementation, so it raises instead of producing a wrong animation.

Source

Thrown at manimlib/mobject/mobject.py:2157


class _AnimationBuilder:
    def __init__(self, mobject: Mobject):
        self.mobject = mobject
        self.overridden_animation = None
        self.mobject.generate_target()
        self.is_chaining = False
        self.methods: list[Callable] = []
        self.anim_args = {}
        self.can_pass_args = True

    def __getattr__(self, method_name: str):
        method = getattr(self.mobject.target, method_name)
        self.methods.append(method)
        has_overridden_animation = hasattr(method, "_override_animate")

        if (self.is_chaining and has_overridden_animation) or self.overridden_animation:
            raise NotImplementedError(
                "Method chaining is currently not supported for " + \
                "overridden animations"
            )

        def update_target(*method_args, **method_kwargs):
            if has_overridden_animation:
                self.overridden_animation = method._override_animate(
                    self.mobject, *method_args, **method_kwargs
                )
            else:
                method(*method_args, **method_kwargs)
            return self

        self.is_chaining = True
        return update_target

    def __call__(self, **kwargs):
        return self.set_anim_args(**kwargs)

View on GitHub (pinned to dee01804d4)

Solutions

  1. Split the chain: play the overridden-animation call first, then chain the rest in a separate animate expression (self.play(mob.animate.a(...)) then self.play(mob.animate.shift(...)))
  2. Reorder so the overridden method is the LAST call in the chain
  3. Replace the overridden call with its non-animated equivalent wrapped in a Transform/ApplyMethod if chaining is essential

Example fix

# before
self.play(mob.animate.move_to(ORIGIN).shift(RIGHT))
# if move_to had an _override_animate in your version -> NotImplementedError

# after
self.play(mob.animate.move_to(ORIGIN))
self.play(mob.animate.shift(RIGHT))
Defensive patterns

Strategy: fallback

Type guard

def has_override_animate(method) -> bool:
    return hasattr(method, "_override_animate")

# check before chaining:
# method = getattr(mob, name)
# if has_override_animate(method): keep it last / play it alone

Try / catch

try:
    builder = mob.animate.move_to(p).shift(RIGHT)
except NotImplementedError:
    builder = mob.animate.shift(RIGHT)  # play overridden call separately

Prevention

When it happens

Trigger: mob.animate.some_overridden_method(...).shift(...) where some_overridden_method has an _override_animate attribute (methods wrapped via the override_animate decorator at mobject.py:2224); chaining two overridden-animation methods: mob.animate.a(...).b(...) where both are overridden; using is_chaining with an overridden method.

Common situations: Long fluent chains in scene code like circle.animate.move_to(x).set_fill(...).scale(2) where one link happens to be an animate-overridden method; library versions that add more _override_animate decorated methods, breaking previously working chains.

Related errors


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