Matplotlib savefig() Saves a Blank Image? 7 Causes and Fixes

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
Matplotlib savefig blank image vs correct: plt.savefig after plt.show and after plt.close save empty white images, while saving before show or with fig.savefig keeps the line chart
The four files the tests below actually saved.

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
Command Prompt screenshot: saving a Matplotlib figure after plt.close() produces a blank image
Real run: after 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
Matplotlib savefig transparent True with dark_background: white chart invisible on a white page versus the same chart saved with its dark facecolor
Left: transparent PNG on white: the white axes and labels vanish. Right: saved with its facecolor.

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].
Matplotlib imshow saving an almost white image because 0-255 float data is clipped, next to the fixed image after dividing by 255
Left: 0–255 floats. Right: divided by 255.

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
Matplotlib savefig legend cut off by default versus included with bbox_inches tight
Left: default. Right: bbox_inches=”tight”, dpi=150.

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
Command Prompt screenshot: Matplotlib savefig raises FileNotFoundError until the folder is created with os.makedirs
The missing-folder error and the fix.

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
Command Prompt screenshot of a Python is_blank helper that checks whether a saved Matplotlib PNG is blank
The is_blank() helper finds the empty file.

Checklist

SymptomCauseFix
Blank after the window was shownplt.savefig() after plt.show()Save before show(), or use fig.savefig()
Blank in a script with several chartsplt.close()/clf() or a new plt.figure() before savingSave first; use fig.savefig()
Blank in Jupytersavefig in a later cellSame cell, or fig.savefig()
Looks empty on a white pagetransparent=True + white linesKeep the facecolor or use a light style
Image almost whiteimshow() float data 0–255Divide by 255 or use uint8
Legend or labels cut offContent outside the figure areabbox_inches="tight"
No fileMissing folder / different working directoryos.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.