Debugging Pillow: mode errors, bombs and truncated files

Diagnose the exceptions Pillow actually raises, disable the decompression-bomb guard deliberately, and handle damaged files without crashing a batch.

The exceptions you will meet

ErrorCauseFix
UnidentifiedImageErrorNot an image, or a format with no pluginValidate the input, check the codec is compiled in
OSError: cannot write mode RGBA as JPEGAlpha channel saved to a format without alphaComposite onto an opaque background
OSError: image file is truncatedIncomplete download or damaged fileRe-fetch, or opt into partial loading deliberately
AttributeError: 'NoneType' object has no attribute 'size'verify() was called and then the image usedReopen the file after verifying
DecompressionBombErrorImage larger than MAX_IMAGE_PIXELSRaise the limit for trusted input, reject otherwise
ValueError: images do not matchSize or mode mismatch in paste or mergeResize or convert before combining
from PIL import Image, UnidentifiedImageError

try:
    im = Image.open(path)
    im.load()                     # forces the decode, so failures happen here
except UnidentifiedImageError:
    print("not an image:", path)
except OSError as exc:
    print("damaged or unsupported:", path, exc)
else:
    print(im.format, im.mode, im.size)

verify() and the reopen rule

# verify() checks the header only, then leaves the object unusable
with Image.open(path) as probe:
    try:
        probe.verify()
    except Exception as exc:
        print("header damaged:", exc)

# check the pixels with a fresh open
with Image.open(path) as im:
    im.load()
    print(im.size)
  • verify() is cheap and catches truncated headers, but not damage in the middle of the file. load() decodes and catches more.
  • After verify() the image object must not be used again; reopen the path.
  • Opening is lazy. A file can open successfully and still fail the moment you read pixels.

Bombs and truncated files

from PIL import Image, ImageFile

# the default guard: a warning above about 89 MP, an error above twice that
print(Image.MAX_IMAGE_PIXELS)

# raise it for trusted, deliberately huge inputs such as maps or scans
Image.MAX_IMAGE_PIXELS = 500_000_000

# or disable the guard entirely: only for input you control
Image.MAX_IMAGE_PIXELS = None

# accept partially downloaded files in a controlled pipeline
ImageFile.LOAD_TRUNCATED_IMAGES = True
⚠️
A decompression bomb is a small file that expands to gigabytes of pixels, and it will exhaust memory before any validation code runs. Never set MAX_IMAGE_PIXELS = None or LOAD_TRUNCATED_IMAGES = True globally in a service that accepts uploads — set them narrowly, process the image, and restore the previous values.

FAQ

The batch crashed on one bad file. How do I keep going?
Catch UnidentifiedImageError and OSError per file, record the path and the reason, and continue. Wrap im.load() inside the try so decode failures are caught too, not just open failures.
Why did open succeed but the image came out black?
The header was valid but the pixel data was incomplete, usually from an interrupted transfer. Setting LOAD_TRUNCATED_IMAGES fills the missing area, which often renders as a grey or black band; the better fix is to re-fetch the file.

Opening, inspecting and saving images Batch image processing scripts and performance

Last refreshed 2026-09-18.