3b1b/manim · error · Exception

Trying to restore without having saved

Error message

Trying to restore without having saved

What it means

Exception('Trying to restore without having saved') raised by Mobject.restore (manimlib/mobject/mobject.py:685). restore() copies a previously captured snapshot via self.become(self.saved_state); if save_state() was never called (or saved_state is None), there is nothing to restore, so manim refuses instead of silently no-op'ing.

Source

Thrown at manimlib/mobject/mobject.py:685

                if value in family:
                    setattr(result, attr, result.family[family.index(value)])
            elif isinstance(value, np.ndarray):
                setattr(result, attr, value.copy())
        return result

    def generate_target(self, use_deepcopy: bool = False) -> Self:
        self.target = self.copy(deep=use_deepcopy)
        self.target.saved_state = self.saved_state
        return self.target

    def save_state(self, use_deepcopy: bool = False) -> Self:
        self.saved_state = self.copy(deep=use_deepcopy)
        self.saved_state.target = self.target
        return self

    def restore(self) -> Self:
        if not hasattr(self, "saved_state") or self.saved_state is None:
            raise Exception("Trying to restore without having saved")
        self.become(self.saved_state)
        return self

    def become(self, mobject: Mobject, match_updaters=False) -> Self:
        """
        Edit all data and submobjects to be idential
        to another mobject
        """
        self.align_family(mobject)
        family1 = self.get_family()
        family2 = mobject.get_family()
        for sm1, sm2 in zip(family1, family2):
            sm1.set_data(sm2.data)
            sm1.set_uniforms(sm2.uniforms)
            sm1.bounding_box[:] = sm2.bounding_box
            sm1.pointlike_uniform_keys = sm2.pointlike_uniform_keys
            sm1.shader_file = sm2.shader_file
            sm1.texture_paths = sm2.texture_paths

View on GitHub (pinned to dee01804d4)

Solutions

  1. Call mob.save_state() once before mutating the mobject, then mob.restore() (or self.play(Restore(mob))) later
  2. Guard with hasattr: if getattr(mob, 'saved_state', None): mob.restore()
  3. If you just want a copy to return to, use generate_target()/target-based Transform instead

Example fix

# before
mob.shift(RIGHT * 2)
self.play(Restore(mob))  # raises: restore without having saved

# after
mob.save_state()
mob.shift(RIGHT * 2)
self.play(Restore(mob))
Defensive patterns

Strategy: validation

Validate before calling

if getattr(mob, 'saved_state', None) is None:
    mob.save_state()
mob.restore()

Prevention

When it happens

Trigger: Calling mob.restore() before any mob.save_state(); accessing the Restore animation (self.play(Restore(mob))) without a prior save_state(); calling restore() twice after resetting saved_state to None.

Common situations: Classic manim pattern `mob.save_state(); mob.transform...; self.play(Restore(mob))` where the first line was dropped during a refactor; reusing a mobject across scenes where the state was never saved in the new scene; code copied from examples without the save_state line.

Related errors


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