plt.savefig() saves a blank image when the figure it saves is no longer the one you drew on. The most common reason is calling it after plt.show(): when you close the plot window, Matplotlib closes the figure, and plt.savefig() then saves a brand-new empty one. The fix is to save first, or to keep the figure and call fig.savefig(). This page reproduces every common cause, measures whether the saved file is really blank, and shows the fix.
Tested with Python 3.12.5 and Matplotlib 3.11.2 on Windows 11 using the interactive TkAgg backend (the default on desktop Python). In the tests, each plot window was closed automatically after 0.8 s, exactly as if you clicked its close button. Blank means every pixel of the saved PNG has the same colour, checked with the is_blank() helper at the end of this page. For the full reference, see the Matplotlib savefig() reference.
Quick fix
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [3, 1, 4, 2])
fig.savefig("chart.png") # 1. save
plt.show() # 2. then show

Cause 1: plt.savefig() after plt.show()
plt.show() blocks until you close the window, and closing it closes the figure. plt.savefig() always saves the current figure, so with none left it creates an empty one and saves that:
Some examples below check the saved file with a small is_blank() helper. Save the helper from the end of this article as blank_check.py in the same folder as your script, so from blank_check import is_blank works.
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [3, 1, 4, 2])
plt.show() # the window opens; the figure is closed when you close it
plt.savefig("after_show.png") # saves a NEW, empty figure
from blank_check import is_blank # the helper at the end of this article
print('after_show.png: blank =', is_blank('after_show.png'))
Output:
after_show.png: blank = True
Swap the two lines and the chart is saved:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [3, 1, 4, 2])
plt.savefig("before_show.png") # save first...
plt.show() # ...then show
from blank_check import is_blank # the helper at the end of this article
print('before_show.png: blank =', is_blank('before_show.png'))
Output:
before_show.png: blank = False
Or keep a reference and use fig.savefig()
fig.savefig() saves that specific figure, which still holds your plot even after its window is closed. This is the most reliable habit; Matplotlib’s documentation for show() notes that the save-before-show rule doesn’t apply when you keep the figure and use Figure.savefig:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [3, 1, 4, 2])
plt.show()
fig.savefig("fig_after_show.png") # the figure object still has the plot
from blank_check import is_blank # the helper at the end of this article
print('fig_after_show.png: blank =', is_blank('fig_after_show.png'))
Output:
fig_after_show.png: blank = False
Cause 2: plt.close() or plt.clf() before saving
plt.close() removes the figure and plt.clf() erases it, so anything saved afterwards is empty:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [3, 1, 4, 2])
plt.close() # or plt.clf()
plt.savefig("after_close.png")
from blank_check import is_blank # the helper at the end of this article
print('after_close.png: blank =', is_blank('after_close.png'))
Output:
after_close.png: blank = True

plt.close() the saved file is blank.Close figures after saving them, for example when you create many charts in a loop:
import matplotlib.pyplot as plt
for i, color in enumerate(["tab:blue", "tab:orange"]):
fig, ax = plt.subplots() # one figure per chart
ax.plot([1, 2, 3], [i, i + 2, i + 1], color=color)
fig.savefig(f"chart_{i}.png")
plt.close(fig) # free memory, AFTER saving
from blank_check import is_blank # the helper at the end of this article
print([is_blank(f"chart_{i}.png") for i in range(2)])
Output:
[False, False]
Cause 3: a new figure became the current figure
plt.figure(), plt.subplots() and some library functions create a new figure and make it current. plt.savefig() then saves that new, empty figure:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [3, 1, 4, 2])
plt.figure() # creates a new, empty "current" figure
plt.savefig("new_figure.png")
from blank_check import is_blank # the helper at the end of this article
print('new_figure.png: blank =', is_blank('new_figure.png'))
Output:
new_figure.png: blank = True
Use fig.savefig() with the figure you drew on, and there’s no “current figure” to get wrong.
Cause 4: Jupyter Notebook: savefig in a later cell
In Jupyter, the inline backend shows and closes every figure at the end of the cell that created it. A plt.savefig() in the next cell therefore saves an empty figure. Call savefig() in the same cell as the plot, or keep the figure and call fig.savefig() later:
# Cell 1
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [3, 1, 2])
# Cell 2
fig.savefig("chart.png") # works: the Figure object still exists
# plt.savefig("chart.png") here would save an empty figure
Cause 5: transparent=True with a dark style
With transparent=True the background is removed. With a dark style such as dark_background, the axes, tick labels and titles are white, so on a white page they vanish and the chart looks (almost) empty, even though the file isn’t blank:
import matplotlib.pyplot as plt
plt.style.use("dark_background") # white axes and text
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [3, 1, 4, 2])
fig.savefig("dark_transparent.png", transparent=True)
fig.savefig("dark_solid.png", facecolor=fig.get_facecolor()) # keep the dark background
from blank_check import is_blank # the helper at the end of this article
print('dark_transparent.png: blank =', is_blank('dark_transparent.png'))
from blank_check import is_blank # the helper at the end of this article
print('dark_solid.png: blank =', is_blank('dark_solid.png'))
Output:
dark_transparent.png: blank = False
dark_solid.png: blank = False

Cause 6: imshow() data out of range (all white)
For RGB images, imshow() expects floats from 0 to 1 or integers from 0 to 255. Floats from 0 to 255 are clipped, so almost every pixel becomes white:
import numpy as np
import matplotlib.pyplot as plt
photo = np.random.default_rng(0).integers(0, 256, size=(40, 60, 3)).astype(float) # 0-255 floats
fig, ax = plt.subplots()
ax.imshow(photo) # floats must be 0-1: almost everything is clipped to white
fig.savefig("imshow_clipped.png")
fig, ax = plt.subplots()
ax.imshow(photo / 255) # scale to 0-1 (or use .astype(np.uint8))
fig.savefig("imshow_fixed.png")
Output (Matplotlib’s warning):
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [0.0..255.0].

Saved image is cut off or blurry
bbox_inches="tight" grows the saved area to include everything, such as a legend placed outside the axes; dpi sets the resolution:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5, 3))
ax.plot([1, 2, 3], [2, 3, 1], label="Very long legend label for the sales series")
ax.legend(loc="upper left", bbox_to_anchor=(1.02, 1)) # legend outside the axes
fig.savefig("legend_cut.png") # legend is cut off
fig.savefig("legend_ok.png", bbox_inches="tight", dpi=150) # everything included, sharper
print("saved")
Output:
saved

plt.savefig() not working: file not saved
If there’s no file at all, check the folder: savefig() doesn’t create missing folders, and a relative path is relative to the folder the script runs from (print os.path.abspath() to see where it went):
import os
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [3, 1, 2])
try:
fig.savefig("charts/output.png") # folder does not exist
except FileNotFoundError as err:
print("FileNotFoundError:", err.strerror)
os.makedirs("charts", exist_ok=True)
fig.savefig("charts/output.png")
print("saved to", os.path.relpath(os.path.abspath("charts/output.png")))
Output:
saved to charts\output.png

A helper to check whether a saved image is blank
Use this in tests or scripts to catch blank charts automatically. It reads the image back and checks whether all pixels are the same colour:
import matplotlib.image as mpimg
import numpy as np
def is_blank(path):
"""True if every pixel of the saved image has the same colour."""
img = mpimg.imread(path)
return bool(np.all(img == img[0, 0]))
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [3, 1, 2])
fig.savefig("ok.png")
plt.close("all")
plt.savefig("empty.png")
print("ok.png blank:", is_blank("ok.png"))
print("empty.png blank:", is_blank("empty.png"))
Output:
ok.png blank: False
empty.png blank: True

is_blank() helper finds the empty file.Checklist
| Symptom | Cause | Fix |
|---|---|---|
| Blank after the window was shown | plt.savefig() after plt.show() | Save before show(), or use fig.savefig() |
| Blank in a script with several charts | plt.close()/clf() or a new plt.figure() before saving | Save first; use fig.savefig() |
| Blank in Jupyter | savefig in a later cell | Same cell, or fig.savefig() |
| Looks empty on a white page | transparent=True + white lines | Keep the facecolor or use a light style |
| Image almost white | imshow() float data 0–255 | Divide by 255 or use uint8 |
| Legend or labels cut off | Content outside the figure area | bbox_inches="tight" |
| No file | Missing folder / different working directory | os.makedirs(), check os.path.abspath() |
More Matplotlib troubleshooting guides:
Frequently asked questions
Why is my plt.savefig() image blank?
Usually because it’s called after plt.show(). Closing the plot window closes the figure, so plt.savefig() saves a new empty one. Call savefig() before show(), or keep the figure and use fig.savefig().
Should I call savefig before or after show?
Before. plt.savefig("chart.png") then plt.show(). If you use fig.savefig() on a figure you kept, the order no longer matters.
Why does savefig save an empty image in Jupyter Notebook?
Jupyter closes each figure at the end of the cell. Save in the same cell as the plot, or keep the fig object and call fig.savefig() in a later cell.
What is the difference between plt.savefig and fig.savefig?
plt.savefig() saves whichever figure is current right now; fig.savefig() saves the figure object you call it on. The second can’t accidentally save the wrong or an empty figure.
How do I check if a saved image is blank?
Read it back with matplotlib.image.imread() and test whether every pixel equals the first one: np.all(img == img[0, 0]), as in the helper above.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn