Fix the “No Handles with Labels” Legend Warning in Python

PYTHON Published Updated 3 mins read By Leon Wei
Fix the “No Handles with Labels” Legend Warning in Python cover image

Quick summary

Summarize this blog with AI

Fix an empty Matplotlib legend in Python

“No handles with labels found to put in legend” is older Matplotlib warning wording. Recent versions may say “No artists with labels found to put in legend.” This is a Python plotting issue: automatic legend discovery found no eligible labelled artists on the selected axes. It is not a base R legend() error.

This tutorial was tested with Python 3.9 and Matplotlib 3.9.4. Install Matplotlib in your Python environment before starting. Run the blocks in order. They reproduce an empty legend, then create a line plot and a two-panel figure. The final block displays the figures after all updates; no external dataset is needed.

Reproduce the warning on the intended axes

import warnings
import matplotlib.pyplot as plt

x = [1, 2, 3]
y = [2, 4, 3]
fig_bad, ax_bad = plt.subplots()
ax_bad.plot(x, y)  # No explicit legend label.
handles, labels = ax_bad.get_legend_handles_labels()
assert handles == [] and labels == []
with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    ax_bad.legend()
assert any("No artists with labels found" in str(w.message) for w in caught)
print(str(caught[0].message))

In the tested version, the warning begins with No artists with labels found to put in legend. The empty handle and label lists explain the problem more reliably than matching warning text across releases. An axis label such as set_xlabel("Day") labels the axis; it does not label a plotted line for the legend.

Label the line before creating the legend

fig, ax = plt.subplots()
line, = ax.plot(x, y, marker="o", label="Daily orders")
ax.set(xlabel="Day", ylabel="Orders", title="Three-day order count")
legend = ax.legend()
fig.tight_layout()
assert [text.get_text() for text in legend.get_texts()] == ["Daily orders"]
print(ax.get_legend_handles_labels()[1])  # ['Daily orders']

At this stage, the figure has points (1, 2), (2, 4), and (3, 3), joined by a line with circular markers. Its legend contains one entry, “Daily orders,” using that line's style. Calling legend() after plotting lets it discover the labelled artist.

Update existing artists and choose the right axes

# Change a label on an existing line, then rebuild its legend.
line.set_label("Completed orders")
legend = ax.legend()
assert [text.get_text() for text in legend.get_texts()] == ["Completed orders"]

# A different subplot has its own artists and legend.
fig_pair, axes = plt.subplots(1, 2)
axes[0].plot(x, y, label="Left panel")
assert axes[1].get_legend_handles_labels() == ([], [])
axes[0].legend()
axes[1].plot(x, [1, 2, 1], label="Right panel")
axes[1].legend()
fig_pair.tight_layout()

For multiple subplots, keep the axes object returned by subplots() and call its methods explicitly. A call to plt.legend() applies to the current axes, which may not be the panel you intended. This is especially easy to miss in a notebook after another plotting command changes the active axes.

Check hidden labels and explicit handles

# Automatic discovery ignores labels that start with an underscore.
hidden, = ax.plot(x, [1, 1, 1], label="_reference", linestyle="--")
discovered_handles, discovered_labels = ax.get_legend_handles_labels()
assert "_reference" not in discovered_labels

# Explicit handles are useful when selecting or ordering visible entries.
legend = ax.legend(handles=[line, hidden], labels=["Orders", "Reference"])
assert [text.get_text() for text in legend.get_texts()] == ["Orders", "Reference"]
fig.tight_layout()

The explicit labels above are nonempty and do not start with an underscore. Keep handles and labels aligned one-for-one; passing only a list of strings relies on artist order and is easier to get wrong. For an ordinary line or scatter plot, assigning label= when creating each artist is usually simpler.

A repeatable debugging sequence

First inspect ax.get_legend_handles_labels() on the exact axes that should own the legend. If it is empty, check that plotting has already happened and that the artist has a usable label. If labels exist on another subplot, fix the axes selection. If you have intentionally selected custom handles, give an explicit matching label for each one. Recreate the legend after changing artist labels.

plt.close(fig_bad)
plt.show()
plt.close(fig)
plt.close(fig_pair)

References

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles