{"record":{"id":"85dd57e4ed92aa34","repo":"3b1b/manim","slug":"cannot-position-endpoints-of-closed-loop","errorCode":null,"errorMessage":"Cannot position endpoints of closed loop","messagePattern":"Cannot position endpoints of closed loop","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"manimlib/mobject/mobject.py","lineNumber":1286,"sourceCode":"    ) -> Self:\n        self.replace(mobject, dim_to_match, stretch)\n        length = mobject.length_over_dim(dim_to_match)\n        self.scale((length + buff) / length)\n        return self\n\n    def put_start_on(self, point: Vect3) -> Self:\n        self.shift(point - self.get_start())\n        return self\n\n    def put_end_on(self, point: Vect3) -> Self:\n        self.shift(point - self.get_end())\n        return self\n\n    def put_start_and_end_on(self, start: Vect3, end: Vect3) -> Self:\n        curr_start, curr_end = self.get_start_and_end()\n        curr_vect = curr_end - curr_start\n        if np.all(curr_vect == 0):\n            raise Exception(\"Cannot position endpoints of closed loop\")\n        target_vect = end - start\n        self.scale(\n            get_norm(target_vect) / get_norm(curr_vect),\n            about_point=curr_start,\n        )\n        self.rotate(\n            angle_of_vector(target_vect) - angle_of_vector(curr_vect),\n        )\n        self.rotate(\n            np.arctan2(curr_vect[2], get_norm(curr_vect[:2])) - np.arctan2(target_vect[2], get_norm(target_vect[:2])),\n            axis=np.array([-target_vect[1], target_vect[0], 0]),\n        )\n        self.shift(start - self.get_start())\n        return self\n\n    # Color functions\n\n    def set_rgba_array(","sourceCodeStart":1268,"sourceCodeEnd":1304,"githubUrl":"https://github.com/3b1b/manim/blob/dee01804d47b9f94402d71472674710dcac125b8/manimlib/mobject/mobject.py#L1268-L1304","documentation":"Exception('Cannot position endpoints of closed loop') raised by Mobject.put_start_and_end_on (manimlib/mobject/mobject.py:1286). The method works by scaling and rotating the vector from get_start() to get_end(); when that vector is zero (start equals end — a closed loop like a Circle, or a degenerate zero-length path) there is no direction or length to scale/rotate, so the math is impossible and manim raises.","triggerScenarios":"Calling put_start_and_end_on(a, b) on a Circle, Ellipse, or any closed curve whose start and end points coincide; calling it on a degenerate VMobject with all points identical; using Line helpers that wrap put_start_and_end_on after points were collapsed.","commonSituations":"Trying to 'stretch' a Circle between two points; generic vector-shape code that assumes open paths; mobjects whose points were scaled to zero or replaced by a single point.","solutions":["Use an open path (Line, Arc, CubicBezier) instead of a closed loop for put_start_and_end_on","For a circle between two points, construct Arc or CurvedArcBetweenPoints-style geometry instead","Check curr_start != curr_end / get_start_and_end() before calling"],"exampleFix":"# before\ncircle = Circle()\ncircle.put_start_and_end_on(LEFT, RIGHT)  # raises: closed loop\n\n# after\nfrom manimlib import ArcBetweenPoints\narc = ArcBetweenPoints(LEFT, RIGHT, angle=TAU)  # open path version\n# or use Line(LEFT, RIGHT) for a straight connector","handlingStrategy":"validation","validationCode":"start, end = mob.get_start_and_end()\nif np.all(end - start == 0):\n    raise ValueError(\"mobject is a closed loop; use Arc/Line instead\")\nmob.put_start_and_end_on(new_start, new_end)","typeGuard":"def is_open_path(mob) -> bool:\n    s, e = mob.get_start_and_end()\n    return not np.all(e - s == 0)","tryCatchPattern":null,"preventionTips":["Check get_start_and_end() differ before calling put_start_and_end_on","Use open paths (Line/Arc/ArcBetweenPoints) for endpoint positioning"],"tags":["manim","mobject","geometry","closed-path"],"backgroundTag":null,"analyzedSha":"dee01804d47b9f94402d71472674710dcac125b8","analyzedAt":"2026-08-14T19:53:44.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}