Matplotlib Legend Warning: No Handles with Labels

Matplotlib prints No handles with labels found to put in legend when legend() cannot find plotted artists with public labels. The common causes are a missing label, a label that starts with an underscore, or a call to legend() before the artists are created. The chart can still render; the automatic legend simply has no entries to show.

Quick answer

Give each plotted line, scatter collection, bar container, or patch a public label, then call ax.legend() after plotting. Matplotlib’s legend API and legend guide document automatic and explicit handle selection.

Matplotlib legend diagram showing missing labels, reliable plot order, handles, labels, and twin axes
Automatic legends need public artist labels before legend() runs; explicit handles provide control for complex plots.

Why the warning happens

Automatic legend collection looks for artists with labels that are not private. A line created without a label has an internal label that begins with an underscore, so Matplotlib intentionally skips it.

import matplotlib.pyplot as plt

figure, axis = plt.subplots()
axis.plot([1, 2, 3], [2, 4, 6])
axis.legend()

Calling legend() in this state produces no useful entry. The order matters too: a legend created before the plotting call cannot automatically include an artist that does not exist yet.

Python Pool infographic showing Matplotlib line, label, artist handle, legend, and plotted output
A legend needs labeled artists or explicit handles that Matplotlib can associate with labels.

Add a label when plotting

The simplest fix is to provide a label at the point where the artist is created.

import matplotlib.pyplot as plt

figure, axis = plt.subplots()
axis.plot([1, 2, 3], [2, 4, 6], label="training")
axis.plot([1, 2, 3], [1, 3, 5], label="validation")
axis.legend()

Use labels that describe the plotted series rather than implementation details. In a loop, assign a label for each series that should appear and use a private label for helper artists that belong in the chart but not in the legend.

Label scatter and bar artists

The same rule applies to scatter plots and bar charts. Pass label to the plotting method or set it on the returned artist before calling legend().

import matplotlib.pyplot as plt

figure, axis = plt.subplots()
axis.scatter([1, 2, 3], [2, 4, 3], label="observations")
axis.bar([1, 2, 3], [1, 3, 2], alpha=0.4, label="baseline")
axis.legend()
Python Pool infographic mapping plot data through label argument to line handle and visible legend entry
Pass a label to the plotted artist before calling legend to create an automatic legend entry.

Inspect handles and labels

When automatic collection is surprising, inspect what Matplotlib sees. The get_legend_handles_labels() method returns the artists and public labels currently available on an axes.

handles, labels = axis.get_legend_handles_labels()
print(labels)
axis.legend(handles, labels)

Explicit handles are useful when labels must be reordered, renamed, or combined from different artists. They are also useful for a custom legend entry that is not directly generated by a plotting call.

Combine legends from two axes

With twin axes, each axes owns its own artists. Collect handles and labels from both axes before passing the combined lists to one legend.

handles_left, labels_left = axis.get_legend_handles_labels()
handles_right, labels_right = right_axis.get_legend_handles_labels()

axis.legend(
    handles_left + handles_right,
    labels_left + labels_right,
)

Remove duplicate labels before the final call when the same series is represented more than once. Keep the legend order aligned with the visual story of the chart.

Keep the fix focused

This warning is usually a setup problem rather than a rendering failure. Check the artist label, the call order, underscore-prefixed labels, and the axes object on which legend() is called. For related chart layout topics, see Matplotlib figsize and Matplotlib imshow().

Python Pool infographic showing line objects, labels list, ax.legend handles labels, and final legend
Explicit handles and labels make the legend relationship clear when automatic discovery is unsuitable.

Use the axes object consistently

Mixing the stateful pyplot API with multiple axes can send the legend call to a different axes than the one containing the labeled artist. The object-oriented form makes ownership explicit: create a figure and axes, call methods on that axes, and call axis.legend() on the same object.

import matplotlib.pyplot as plt

figure, axis = plt.subplots()
axis.plot([1, 2, 3], [2, 4, 6], label="series A")
axis.legend()

In a notebook, cells may also be run out of order. Re-run the plotting cell from top to bottom after changing labels, and clear an old figure when stale artists make the result confusing.

Label series inside a loop

When plotting several arrays in a loop, give each visible series a stable label. Do not call legend() on every iteration; create all artists first and build the legend once. This keeps the legend from being repeatedly rebuilt and makes duplicate entries less likely.

import matplotlib.pyplot as plt

figure, axis = plt.subplots()
series = {"train": [1, 3, 5], "test": [2, 2, 4]}

for name, values in series.items():
    axis.plot([1, 2, 3], values, label=name)

axis.legend()

If several helper artists represent one conceptual series, label only the representative artist and give the others a private label such as _nolegend_. That keeps the visual chart detailed without making the legend repetitive.

Python Pool infographic testing underscore labels, empty artists, order, duplicate labels, and validation
Check hidden underscore labels, empty plots, artist order, duplicate labels, and the axes that owns the legend.

Create explicit custom entries

Sometimes the legend should describe a threshold, region, or category that is not a normal plotted line. Create a handle such as a Line2D object and pass it with the desired label. Explicit handles are also useful when the automatic order does not match the explanation in the surrounding text.

from matplotlib.lines import Line2D

threshold = Line2D([], [], color="red", linestyle="--", label="threshold")
axis.legend(handles=[threshold])

Keep custom handles synchronized with the chart’s colors, line styles, and marker choices. A legend is part of the data explanation, so a technically valid entry that does not match the plot can still mislead readers.

Deduplicate before finalizing

Repeated calls or repeated labels can create duplicate entries. Retrieve the handles and labels, preserve the first occurrence of each label, and pass the filtered lists explicitly when the plot contains many layers. Do not remove duplicates when the same label intentionally refers to separate visual encodings; document that choice instead.

After changing a label, inspect the rendered legend rather than checking only that the warning disappeared. Confirm that each entry maps to the correct color, marker, and line style, and that the legend remains readable at the final figure size. A silent legend with the wrong mapping is a data-communication bug even when Matplotlib reports no error.

Verify the final figure at the same size used in reports or exported files, because a legend that fits interactively can still collide with the axes when saved.

Include one visual regression check for the legend position and text when the chart is part of a generated report.

Frequently Asked Questions

Why does Matplotlib say no handles with labels were found?

legend() cannot find plotted artists with public labels, often because labels are missing, private, or created after the legend call.

How do I fix the Matplotlib legend warning?

Pass label to each plotted artist and call ax.legend() after the artists have been added to the axes.

How do I inspect Matplotlib legend handles?

Call ax.get_legend_handles_labels() to retrieve the artists and public labels currently available for automatic collection.

How do I combine legends from twin axes?

Collect handles and labels from both axes, concatenate the lists, and pass them explicitly to one legend call.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted