{"record":{"id":"cdf723bf7b6614b2","repo":"3b1b/manim","slug":"movetotarget-called-on-mobject-without-attribute","errorCode":null,"errorMessage":"MoveToTarget called on mobject without attribute 'target'","messagePattern":"MoveToTarget called on mobject without attribute 'target'","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"manimlib/animation/transform.py","lineNumber":145,"sourceCode":"class ReplacementTransform(Transform):\n    replace_mobject_with_target_in_scene: bool = True\n\n\nclass TransformFromCopy(Transform):\n    replace_mobject_with_target_in_scene: bool = True\n\n    def __init__(self, mobject: Mobject, target_mobject: Mobject, **kwargs):\n        super().__init__(mobject.copy(), target_mobject, **kwargs)\n\n\nclass MoveToTarget(Transform):\n    def __init__(self, mobject: Mobject, **kwargs):\n        self.check_validity_of_input(mobject)\n        super().__init__(mobject, mobject.target, **kwargs)\n\n    def check_validity_of_input(self, mobject: Mobject) -> None:\n        if not hasattr(mobject, \"target\"):\n            raise Exception(\n                \"MoveToTarget called on mobject without attribute 'target'\"\n            )\n\n\nclass _MethodAnimation(MoveToTarget):\n    def __init__(self, mobject: Mobject, methods: list[Callable], **kwargs):\n        self.methods = methods\n        super().__init__(mobject, **kwargs)\n\n\nclass ApplyMethod(Transform):\n    def __init__(self, method: Callable, *args, **kwargs):\n        \"\"\"\n        method is a method of Mobject, *args are arguments for\n        that method.  Key word arguments should be passed in\n        as the last arg, as a dict, since **kwargs is for\n        configuration of the transform itself\n","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/3b1b/manim/blob/dee01804d47b9f94402d71472674710dcac125b8/manimlib/animation/transform.py#L127-L163","documentation":"MoveToTarget animates a mobject toward its pre-generated .target copy. The mobject.target attribute is created only by Mobject.generate_target(); MoveToTarget.check_validity_of_input raises immediately in the constructor if it is absent.","triggerScenarios":"Playing MoveToTarget(mob) without a preceding mob.generate_target(); or calling mob.generate_target() on a different mobject than the one passed to MoveToTarget.","commonSituations":"Classic manim idiom is mob.generate_target(); mob.target.shift(RIGHT); self.play(MoveToTarget(mob)) — forgetting the first line is by far the most common hit; also after re-creating the mobject variable the .target is lost.","solutions":["Call mobject.generate_target() first, mutate mobject.target, then self.play(MoveToTarget(mobject))","If you don't need the two-step idiom, use Transform(mob, mob.copy().shift(RIGHT)) or mob.animate.shift(RIGHT) instead"],"exampleFix":"# before\nself.play(MoveToTarget(circle))\n# after\ncircle.generate_target()\ncircle.target.scale(2)\nself.play(MoveToTarget(circle))","handlingStrategy":"validation","validationCode":"if not hasattr(mobject, \"target\") or mobject.target is None:\n    mobject.generate_target()\nmobject.target.shift(RIGHT)\nself.play(MoveToTarget(mobject))","typeGuard":"def has_target(mobject) -> bool:\n    return getattr(mobject, \"target\", None) is not None","tryCatchPattern":null,"preventionTips":["Always pair generate_target() and MoveToTarget in adjacent lines","Prefer mob.animate or mob.animate.interpolate(...) for one-shot transforms — no target attribute needed"],"tags":["animation","transform","mobject","validation"],"backgroundTag":null,"analyzedSha":"dee01804d47b9f94402d71472674710dcac125b8","analyzedAt":"2026-08-14T19:53:44.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}