2025, Oct 15 20:00
How to Manage Multiple Matplotlib Legends and Remove Them Safely without Duplicate add_artist Calls
Learn why ax.legend replaces existing legends in Matplotlib and how to avoid duplicate add_artist calls so remove() works reliably when using multiple legends.
Managing multiple legends in Matplotlib can be trickier than it looks. A common scenario is to create two legends on the same axes, only to discover that removing them later behaves inconsistently: one legend disappears, another stubbornly remains, or a generic cleanup loop throws an exception. The root cause is not in remove itself but in how legends are created and attached to the axes.
Reproducing the issue
The following example builds a plot with two legends and ends up with one of them registered twice, which is what makes removal unreliable.
import matplotlib.pyplot as plt
canvas, axes = plt.subplots()
# sample data
xs = [1, 2, 3, 4, 5]
vals_a = [1, 4, 9, 16, 25]
vals_b = [25, 16, 9, 4, 1]
axes.plot(xs, vals_a, label='y = x^2')
axes.plot(xs, vals_b, label='y = 25 - x^2')
# initial legend just to get handles/labels
axes.legend()
hnds, lbls = axes.get_legend_handles_labels()
# remove the temporary legend
axes.get_legend().remove()
# build two legends from the collected handles/labels
split_idx = len(hnds) // 2
legend_left = axes.legend(hnds[:split_idx], lbls[:split_idx], loc="upper left")
legend_right = axes.legend(hnds[split_idx:], lbls[split_idx:], loc="lower right")
# explicitly attach both
axes.add_artist(legend_left)
axes.add_artist(legend_right)
What’s really happening
Understanding what axes.legend does explains the odd behavior. When you call ax.legend, it creates a legend, attaches it to the axes, and removes any existing legend that was already there. After the second call in the example above, only the second legend remains attached; the first one has been automatically removed. By calling add_artist for both legend objects afterward, the first legend is added once, while the second legend ends up on the axes twice. That duplication is why one remove() appears to do nothing for the second legend and why a naive cleanup loop over children can hit a ValueError when it tries to remove the same object more than once.
Calling ax.legend creates a legend, adds it to the axes, and replaces any existing legend in the process. If you later use add_artist to attach legends manually, make sure each legend is added only once; otherwise a legend can be present multiple times.
The fix
The crucial change is simple: do not re-add the second legend. It is already attached by the second axes.legend call. With each legend present exactly once, remove() works cleanly for both.
import matplotlib.pyplot as plt
canvas, axes = plt.subplots()
xs = [1, 2, 3, 4, 5]
vals_a = [1, 4, 9, 16, 25]
vals_b = [25, 16, 9, 4, 1]
axes.plot(xs, vals_a, label='y = x^2')
axes.plot(xs, vals_b, label='y = 25 - x^2')
# build a temporary legend to collect handles/labels
axes.legend()
hnds, lbls = axes.get_legend_handles_labels()
axes.get_legend().remove()
# create two legends
split_idx = len(hnds) // 2
legend_ul = axes.legend(hnds[:split_idx], lbls[:split_idx], loc="upper left")
legend_lr = axes.legend(hnds[split_idx:], lbls[split_idx:], loc="lower right")
# attach only the first one explicitly; the second is already attached by legend()
axes.add_artist(legend_ul)
# later: remove both legends reliably
legend_ul.remove()
legend_lr.remove()
Why this matters
Legend management in Matplotlib is stateful. Each call to axes.legend modifies the axes by replacing the current legend. Mixing that behavior with manual add_artist calls can easily lead to duplicated legend objects. Duplicates are hard to notice visually but can cause partial removals or exceptions during cleanup, for example when iterating over axes.get_children() and trying to remove the same legend object more than once.
Takeaways
Be mindful that axes.legend both creates and attaches a legend while removing any previous one. If you need multiple legends, attach only the ones that are not already on the axes. With each legend present exactly once, remove() works as intended. If you automate cleanup, ensure you are not attempting to remove identical legend objects multiple times; keeping track of the legend instances you add avoids that pitfall.
The article is based on a question from StackOverflow by BernhardWebstudio and an answer by RuthC.