matplotlib axis labels not showing (tested fixes)
Use ax.set_xlabel("..."), not ax.xlabel = "..."; plt.xlabel only labels the last subplot; and a label outside the figure is cropped until you call fig.tight_layout().
Axis labels that do not appear have three common causes: an assignment instead of a call, plt.xlabel on the wrong subplot, and a label drawn outside the figure. Measured with matplotlib 3.11.2 on Python 3.13.5 (Agg backend); outputs are unedited.
1. ax.xlabel = "..." sets nothing
Code:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(4, 3))
ax.plot([1, 2, 3])
ax.xlabel = "time" # assigns an attribute, sets nothing
print("ax.xlabel = ... ->", repr(ax.get_xlabel()))
try:
ax.xlabel("time")
except TypeError as e:
print("TypeError:", e)
plt.xlabel = "time" # overwrites the function itself
try:
plt.xlabel("time")
except TypeError as e:
print("TypeError:", e)
Output:
ax.xlabel = ... -> ''
TypeError: 'str' object is not callable
TypeError: 'str' object is not callable
The Axes method is ax.set_xlabel("time"). Assigning ax.xlabel = "time" just creates a plain attribute: no error, and get_xlabel() stays empty; calling it afterwards gives TypeError: 'str' object is not callable. Writing plt.xlabel = "time" is worse: it replaces the pyplot function for the rest of the session (in Jupyter, restart the kernel).
2. Subplots and labels cut off
Code:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, (a1, a2) = plt.subplots(1, 2, figsize=(4, 3))
a1.plot([1, 2]); a2.plot([2, 1])
plt.xlabel("seconds") # only the current (last) Axes
print("left:", repr(a1.get_xlabel()), "right:", repr(a2.get_xlabel()))
a1.set_xlabel("seconds"); a1.set_ylabel("value")
fig, ax = plt.subplots(figsize=(4, 3))
ax.set_xlabel("Time (s)", fontsize=30)
fig.savefig("big.png")
fig.canvas.draw()
r = fig.canvas.get_renderer()
bb = ax.xaxis.label.get_window_extent(r)
print("label bottom y (px):", round(bb.y0), "-> inside figure:", bb.y0 >= 0)
fig.tight_layout()
fig.canvas.draw()
bb = ax.xaxis.label.get_window_extent(r)
print("after tight_layout:", round(bb.y0), "-> inside figure:", bb.y0 >= 0)
Output:
left: '' right: 'seconds'
label bottom y (px): -38 -> inside figure: False
after tight_layout: 15 -> inside figure: True
plt.xlabel() labels only the current Axes, the last subplot created, so the left plot stayed unlabelled; call set_xlabel/set_ylabel on each Axes. A large label on a small figure can sit below the bottom edge (y = -38 px here), so it is cropped in the window and in savefig. fig.tight_layout() moved it inside (y = 15 px); plt.subplots(layout="constrained") or savefig(..., bbox_inches="tight") also work.
Related: matplotlib legend not showing, matplotlib not showing plot, matplotlib savefig.
Frequently asked questions
Why does plt.xlabel say str object is not callable?
You (or an earlier cell) assigned plt.xlabel = "...", replacing the function. Restart the kernel and call plt.xlabel("...").
Why is only one subplot labelled?
plt.xlabel targets the current Axes only; call ax.set_xlabel on each subplot.
Why are my axis labels cut off?
They sit outside the figure; use fig.tight_layout(), layout="constrained" or bbox_inches="tight".
Written with AI assistance. Measured on 2026-10-23 with matplotlib 3.11.2 and CPython 3.13.5 on Linux (Agg backend). Every output is the verbatim result of running the code shown. PlainTables is our extension; matplotlib is free.