This matplotlib legend error means you called get_legend_handles_labels() on a None object; point to a real Axes and build the legend correctly.
Hitting this legend trace stops plots cold during a demo or a CI job. The message points to one thing: Python tried to access get_legend_handles_labels() on something that is None instead of a live matplotlib object. The good news is that the fix is straight. Make sure you are working with the right Axes, create the legend on that same axes, and avoid calling legend helpers before any artists exist.
What The Error Actually Means
Quick check: read the line that raised the error. If it says ax.get_legend_handles_labels() or a library called the same on your behalf, your variable ax is None or you passed None into a helper that expects an Axes.
Matplotlib builds legends from handles and labels attached to an axes. Calling ax.legend() after you have plotted lines or patches is shorthand for grabbing those handles and labels and creating the legend. If ax is not a real axes, legend creation fails with this message. The same happens if a plotting wrapper returns a grid or figure while you still try to pull legend data from a missing axes.
# Wrong: ax is None, so this crashes
ax = None
handles, labels = ax.get_legend_handles_labels() # <-- AttributeError
Common Causes And The Fix That Solves Each One
Quick scan: match your situation with the list below, then apply the one-line fix.
- Using A Wrapper That Returns A Grid, Not An Axes — functions such as seaborn’s
catplotreturn aFacetGrid, not anAxes. Pull the axes from the grid or passax=into seaborn calls so you keep control of the target axes. - Forgetting To Create A Legend — calling
ax.get_legend()or querying handles before any legend exists leads to None. First callax.legend()after plotting. - Plotting On One Axes, Asking Another For A Legend — you drew on
ax1but requested handles fromax2. Always grab handles from the axes that holds your artists. - Overwriting
axAccidentally — reassigningax = ax.legend(...)is a common slip;legend()returns aLegend, not anAxes. Keepaxintact and store the legend inleg. - Empty Labels On Artists — artists with labels that start with
"_"or empty strings are ignored by the legend. Add clean labels or pass handles and labels manually.
Minimal Working Pattern That Never Crashes
import matplotlib.pyplot as plt
fig, ax = plt.subplots() # 1) create the Axes
ax.plot([0, 1], [0, 1], label="Line A") # 2) add artists with labels
ax.plot([0, 1], [1, 0], label="Line B")
ax.legend() # 3) build legend from handles/labels
plt.show()
Seaborn Users: Keep Control Of ax
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots()
sns.violinplot(data=[[1,2,3],[2,3,4]], ax=ax) # send plotting into your Axes
ax.legend(handles=ax.lines, labels=["Group 1","Group 2"])
plt.show()
Fixing AttributeError: ‘NoneType’ Object Has No Attribute ‘Get_Legend_Handles_Labels’
Deeper fix: pick the snippet that matches your stack. Each one protects ax and ensures legend data exists.
1) You Used A Function That Returns Figure, Axes
# Right: keep fig, ax; never overwrite ax
fig, ax = plt.subplots()
ax.plot(x, y, label="Series")
leg = ax.legend()
- Keep The Axes Variable — never reassign
axto a legend object. - Call Legend After Artists — build the legend once data is drawn.
2) You Plotted With Seaborn’s Figure-Level Function
# FacetGrid returns .ax or .axes (array)
g = sns.catplot(data=df, x="day", y="total_bill", hue="sex", kind="violin")
ax = g.ax # single-axes case
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
- Extract The Axes — read from
g.axor iterateg.axes.flatten()for multi-panel plots. - Or Supply
ax=Up Front — route drawing into a known axes so you never chase it later.
3) You Built Artists But Labels Were Empty
# Artists with labels starting "_" are skipped
line, = ax.plot(x, y, label="_raw")
# Fix: give a clean label or pass handles/labels explicitly
ax.legend(handles=[line], labels=["Raw"])
- Give Real Labels — avoid labels that start with an underscore.
- Pass Handles Manually — supply
handlesandlabelswhen you need full control.
4) You Queried Legend State Before Any Artists
# Wrong order
leg = ax.get_legend() # returns None if no legend exists
handles, labels = ax.get_legend_handles_labels() # may fail
# Right order
ax.plot(x, y, label="Data")
ax.legend()
- Plot First — draw at least one labeled artist.
- Create The Legend — then fetch it or tweak it.
Fast Diagnostic Table
| Symptom | Likely Cause | Direct Fix |
|---|---|---|
Trace mentions get_legend_handles_labels on line with ax. |
ax is None or points to the wrong axes. |
Assign the right axes and keep it alive with fig, ax = plt.subplots(). |
| Legend shows nothing. | Artists have empty or "_" labels. |
Set readable labels or pass handles and labels to ax.legend(). |
| Crash after seaborn call. | Used a figure-level function; axes not obvious. | Pull ax from the grid or pass ax= when plotting. |
Clean Patterns For Multi-Axes And Proxies
Quick check: if you have multiple subplots, always call legend on the subplot that owns the artists. If you need a single legend for a figure, collect handles across axes.
fig, axs = plt.subplots(1, 2, sharey=True)
h1, l1 = axs[0].get_legend_handles_labels()
h2, l2 = axs[1].get_legend_handles_labels()
fig.legend(h1 + h2, l1 + l2, loc="lower center", ncol=2)
Some artists do not appear in legends by default. Build a small proxy using a line or patch with the look you want, then pass it with a readable label.
from matplotlib.lines import Line2D
proxy = Line2D([0], [0], linewidth=6, color="tab:orange")
ax.legend([proxy], ["Prediction Band"])
Reliable Guard Rails To Prevent The Error Next Time
- Pin Down The Axes Variable — always create plots with
fig, ax = plt.subplots()and passaxthrough helpers. Do not reassignax. - Label When You Plot — add
label=at plot time so legend creation is one line. - Build The Legend Once — call
ax.legend()after artists exist; if you update data, rebuild the legend explicitly. - Be Careful With Figure-Level APIs — when a library returns a grid, extract the axes you need or pass
ax=to keep the flow simple. - Handle Ignored Labels — avoid labels that start with
"_"; pass handles and labels directly when needed.
Why These Steps Work
Under the hood, ax.legend() is the same as fetching handles and labels from the axes and sending them back into the legend constructor. If you ask a None object for those lists, you get this AttributeError. By keeping a valid axes in scope and creating the legend after adding artists, you give matplotlib the data it expects, and the call succeeds on every run.
Copy-Ready Snippets For CI Logs And Notebooks
Assert Axes Is Not None Before Legend Calls
def safe_legend(ax):
assert ax is not None, "Expected a Matplotlib Axes, got None"
h, l = ax.get_legend_handles_labels()
if not h:
return None
return ax.legend(h, l)
One Figure, One Legend Across Subplots
def figure_legend(fig, axes):
handles, labels = [], []
for a in axes:
h, l = a.get_legend_handles_labels()
handles.extend(h); labels.extend(l)
return fig.legend(handles, labels, bbox_to_anchor=(0.5, 0.02), loc="lower center", ncol=3)
Where The Error Shows Up In Real Projects
You might hit the same message while plotting violin charts with third-party wrappers. The stack trace often reveals that a helper tried to call ax.get_legend_handles_labels() while the axes reference was None. Directing the plot into your own axes or upgrading to a patched release usually clears it.
Use The Exact Keyword Where It Matters
For searchers landing here with logs that literally read AttributeError: ‘NoneType’ object has no attribute ‘get_legend_handles_labels’, the root cause is still the same: a missing or wrong axes reference. Set up the axes, add labeled artists, and create the legend on that same axes. If the plot wrapper hides the axes, grab it from the object it returns, then call legend() on it. Once this flow is in place, the string AttributeError: ‘NoneType’ Object Has No Attribute ‘Get_Legend_Handles_Labels’ falls out of your logs for good.
