Skip to content

[Bug]: Intermittent ValueError: The passed figure is not managed by pyplot with ipympl (%matplotlib widget) #622

@michitaro

Description

@michitaro

Bug summary

When using Matplotlib with ipympl (%matplotlib widget) in JupyterLab, I intermittently get:

ValueError: The passed figure is not managed by pyplot

This happens with standard pyplot usage (plt.figure, plt.subplots(num=fig), plt.sca(ax)), without explicitly constructing canvases.

Code for reproduction

%matplotlib widget
import time
import matplotlib.pyplot as plt

def try_once():
    fig_num = 1
    plt.close(fig_num)
    fig = plt.figure(fig_num)

    fig, axs = plt.subplots(10, 10, num=fig, squeeze=False)

    for ax in axs.flat:
        plt.sca(ax)  # <-- intermittently raises
        plt.plot([1, 2, 3])
        fig.canvas.draw_idle()
        time.sleep(0.001)

for i in range(300):
    try:
        try_once()
    except Exception as e:
        print('FAILED at iteration', i, '->', repr(e))
        raise

Actual outcome

Image

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[2], line 20
     18 for i in range(300):
     19     try:
---> 20         try_once()
     21     except Exception as e:
     22         print('FAILED at iteration', i, '->', repr(e))

Cell In[2], line 13, in try_once()
     10 fig, axs = plt.subplots(10, 10, num=fig, squeeze=False)
     12 for ax in axs.flat:
---> 13     plt.sca(ax)  # <-- intermittently raises
     14     plt.plot([1, 2, 3])
     15     fig.canvas.draw_idle()

File ~/ipympl-debug/issue/.venv/lib/python3.12/site-packages/matplotlib/pyplot.py:1376, in sca(ax)
   1372 # Mypy sees ax.figure as potentially None,
   1373 # but if you are calling this, it won't be None
   1374 # Additionally the slight difference between `Figure` and `FigureBase` mypy catches
   1375 fig = ax.get_figure(root=False)
-> 1376 figure(fig)  # type: ignore[arg-type]
   1377 fig.sca(ax)

File ~/ipympl-debug/issue/.venv/lib/python3.12/site-packages/matplotlib/pyplot.py:995, in figure(num, figsize, dpi, facecolor, edgecolor, frameon, FigureClass, clear, **kwargs)
    993 root_fig = num.get_figure(root=True)
    994 if root_fig.canvas.manager is None:
--> 995     raise ValueError("The passed figure is not managed by pyplot")
    996 elif (any(param is not None for param in [figsize, dpi, facecolor, edgecolor])
    997       or not frameon or kwargs) and root_fig.canvas.manager.num in allnums:
    998     _api.warn_external(
    999         "Ignoring specified arguments in this call because figure "
   1000         f"with num: {root_fig.canvas.manager.num} already exists")

ValueError: The passed figure is not managed by pyplot

Expected outcome

Mosaicked plots will be shown without errors.

Additional information

Observed state at failure

At the moment the exception occurs:

  • root_fig.canvas.manager is None
  • but the corresponding FigureManager still exists in matplotlib._pylab_helpers.Gcf (so pyplot still manages the figure)

So pyplot’s “managed figure” check is making a false negative.

Where the exception comes from

plt.sca(ax) calls pyplot.figure(fig) internally.
When passed a Figure, pyplot.figure() currently checks only:

root_fig = num.get_figure(root=True)
if root_fig.canvas.manager is None:
    raise ValueError("The passed figure is not managed by pyplot")

In this bug, canvas.manager is temporarily/incorrectly None even though the manager still exists in Gcf.

Proposed fix

If root_fig.canvas.manager is None, fall back to a Gcf lookup to find the manager whose m.canvas.figure is root_fig, and reattach it.

manager = root_fig.canvas.manager
if manager is None:
    for m in _pylab_helpers.Gcf.get_all_fig_managers():
        if getattr(getattr(m, "canvas", None), "figure", None) is root_fig:
            manager = m
            root_fig.canvas.manager = m
            break
if manager is None:
    raise ValueError(...)

This same logic eliminates the failures in my environment (0/60) when applied as a small monkey patch around pyplot.figure().

This resembles matplotlib/matplotlib#19380 (same structural cause: FigureCanvasBase.__init__ sets self.manager=None after replacing figure.canvas).

Operating system

Ubuntu 24

Matplotlib Version

3.10.8

ipympl Version

0.10.0

Matplotlib Backend

widget

Python version

Python 3.12.3

Jupyter version

jupyter lab --version: 4.5.4

Installation

uv

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions