Upgrading pillow 12 to 13
Breaking12.3.0 → 13.0.0Breaking4Security53Platform2Deprecated4New8Changed84Fixed113Other105
SecurityHigh · CVSS 7.1Pillow: TIFF encoder ColorMap plane-size mismatch causes a heap out-of-bounds readGHSA-pchr-8j38-3x4p · CVE-2026-112896 ↗
BreakingUpgrade verdict
4 breaking changes and 2 platform requirement changes and 47 security fixes.
In plain terms
- APIs that were deprecated in earlier releases have now been removed.
- Code that expected the XMP metadata value to be a tuple will now get a different type.
- An internal, undocumented helper for saving GIFs as Netpbm files is gone.
- The Makernote entry in the ExifTags IFD enum is no longer available.
Must act Top 5 of 57 that need code or config changes
Python requirement changed in 13.0.0: >=3.10 → >=3.11.
What you must change
Breaking4
Removes deprecations scheduled for Pillow 13.0.0.
XMP info is no longer returned as a tuple.
Image.infoRemoves the undocumented GifImagePlugin._save_netpbm function.
GifImagePlugin._save_netpbmRemoves ExifTags.IFD.Makernote.
ExifTags.IFD.MakernoteSecurity47
Pillow: TIFF encoder ColorMap plane-size mismatch causes a heap out-of-bounds read
High · CVSS 7.1Pillow: Heap buffer overflow in _tiffWriteProc when saving TIFF to BytesIO
_tiffWriteProcBytesIOCriticalApplies uclouvain/openjpeg#1621 to address OSV-2025-219.
Rejects JPEG streams whose scanlines are wider or have more components than expected.
Pillow: Heap over-read in WMF/EMF rendering via 16-bit dimension truncation (Windows builds)
High · CVSS 8.1Fixes a use-after-free when a libtiff encode error occurs.
Pillow: EPS load(scale) lets Ghostscript render past the decompression bomb limit
ModerateAdds a decompression bomb check to grabclipboard() on Windows.
grabclipboard()Pillow: Decompression bomb check bypass in SpiderImagePlugin.seek() leads to unbounded allocation DoS
Medium · CVSS 6.5Fixes an out-of-bounds read in C getlist() and _putdata() when __len__ overstates length.
getlist()_putdata()__len__Pillow: Decompression-bomb protection bypass via multi-frame seek() in MPO and DCX (MpoImageFile.seek / DcxImageFile.seek)
Medium · CVSS 6.5Adds a decompression bomb check when loading WmfStubImageFile at a different DPI.
WmfStubImageFilePillow tiled-TIFF decoder reads past tile buffer in copy4 (Unpack.c:1290)
Moderate · CVSS 6.5Pillow: Out-of-bounds access in _imagingmorph on a zero-width image
High · CVSS 7.5Pillow: GIF seek keeps rejected size after DecompressionBombError, allowing DoS
HighPillow: Heap buffer overflow in ImagingJpegDecode
Critical · CVSS 9.8Pillow: OOB read in TiffDecode.c tile and RGBA paths leaks heap memory into pixels
HighPillow: Unbounded recursion parsing a PDF page tree or trailer chain (incomplete fix for CVE-2026-42310)
HighPillow: TiffDecode returns uninitialized memory as pixels for short JPEG strips
HighPillow: APNG background disposal allocates full canvas before decompression bomb check
HighPillow: Quadratic GIF comment concatenation allows DoS in Image.open()
ModeratePillow: Infinite loop in MIC n_frames when the embedded TIFF
HighPillow: PdfParser can recurse without limit in get_value() and linearize_page_tree()
ModeratePillow: Bundled libopenjp2 2.5.4 in all wheels since 11.1.0 carries OSV-2025-219 heap-buffer-overflow write reachable from Image.open().load()
High · CVSS 8.1Pillow: NULL dereference in ImagingExpand via rank filters on zero-size images
High · CVSS 7.5Pillow: P;2L and P;4L decoders may read 1 to 3 bytes past the passed buffer size
High · CVSS 8.2Pillow: Heap use-after-free when encoding TIFF to an in-memory buffer
High · CVSS 7.5Pillow: Double free in ImagingScaleAffine on calloc failure via Image.transform
High · CVSS 7.5Pillow: NULL pointer dereference in Image.merge via an unvalidated band 0
High · CVSS 7.5Pillow: Infinite loop in PdfParser.rewrite_pages on page tree Parent cycle
HighPillow: Quadratic GIF comment copying during save
ModeratePillow: MSP RLE decoder expands a tiny file into unbounded memory
HighPillow: Invalid free in FreeType glyph rendering when a stroker is active
HighPillow: Quadratic-time DoS in PDF save_all (PdfParser.next_object_id)
HighPillow: Image.quantize() with empty palette leaks uninitialized stack memory
ModeratePillow: Argument Injection in Ghostscript call, potentially leading into RCE
CriticalPillow: ReDoS in XbmImagePlugin: 512-byte file stalls Image.open() for minutes
High · CVSS 7.5Pillow: Memory disclosure and segfault in libtiff encoder via crafted TIFF tags
HighPillow: Heap buffer overflow writing JPEG comment after ICC profile fills buffer
HighPillow: Reachable undefined behavior in native integer operations, potentially leading to denial of service or logic integrity compromise
Medium · CVSS 6.5Pillow: BMP RLE decoder memory-amplification DoS via unbounded delta escape / absolute mode
Moderate · CVSS 6.5Pillow multi-frame loaders can reuse stale native image storage after mode transitions
Critical · CVSS 9.8Pillow PDF append accepts malformed shared page-tree DAG causing resource exhaustion
HighPillow: TarIO negative size field causes infinite CPU-spin loop
Medium · CVSS 6.5Pillow: PNG eXIf chunk makes EXIF parsing run in quadratic time
Medium · CVSS 6.5Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution
Critical · CVSS 9.8Pillow: Arrow export leaks stale heap memory when PILLOW_ALIGNMENT > 1
Medium · CVSS 5.3Platform2
Drops support for Python 3.10.
Adds support for Python 3.15.
Deprecations to plan for
Deprecated4
Deprecates the IM image format.
Deprecates ImageQt align8to32() in favour of the raw encoder stride.
ImageQt.align8to32()Deprecates JpegImageFile.load_djpeg.
JpegImageFile.load_djpegRemoves the type hint for the deprecated im.putdata(im.getdata()) pattern.
Image.putdata()Image.getdata()API changes
Generated by DepDelta from published Python source. Not mixed with prose notes.
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- removed:
field IFD.Makernote
- changed:
method FontFile.save(filename: str) -> None → method FontFile.save(filename: str | os.PathLike[str]) -> None - added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- changed:
static method GimpPaletteFile.frombytes(data: bytes) -> GimpPaletteFile → static method GimpPaletteFile.frombytes(data: bytes) -> Self
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- removed:
method Image.get_child_images() -> list[ImageFile.ImageFile] - removed:
variable ElementTree - removed:
variable ElementTree: ModuleType | None - removed:
variable logger - changed:
function frombuffer(mode: str, size: tuple[int, int], data: bytes | SupportsArrayInterface, decoder_name: str=…, *args: Any) -> Image → function frombuffer(mode: str, size: tuple[int, int], data: bytes | bytearray | SupportsArrayInterface, decoder_name: str=…, *args: Any) -> Image - changed:
method Image.convert(mode: str | None=…, matrix: tuple[float, ...] | None=…, dither: Dither | None=…, palette: Palette=…, colors: int=…) -> Image → method Image.convert(mode: str | None=…, matrix: list[float] | tuple[float, ...] | None=…, dither: Dither | None=…, palette: Palette=…, colors: int=…) -> Image - changed:
method Image.getxmp() -> dict[str, Any] → method Image.getxmp(*, strip_namespaces: bool=…) -> dict[str, Any] - changed:
method Image.putdata(data: Sequence[float] | Sequence[Sequence[int]] | core.ImagingCore | NumpyArray, scale: float=…, offset: float=…) -> None → method Image.putdata(data: Sequence[float] | Sequence[Sequence[int]] | NumpyArray, scale: float=…, offset: float=…) -> None - added:
field Resampling.MKS2013 - added:
field Resampling.MKS2021
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- changed:
field Axis.name: bytes | None → field Axis.name: NotRequired[bytes] - changed:
function load(filename: str) -> ImageFont → function load(filename: str | os.PathLike[str]) -> ImageFont - changed:
function load_path(filename: str | bytes) -> ImageFont → function load_path(filename: StrOrBytesPath) -> ImageFont
- changed:
function crop(image: Image.Image, border: int=…) -> Image.Image → function crop(image: Image.Image, border: int | tuple[int, ...]=…) -> Image.Image - added:
variable TYPE_CHECKING
- changed:
method ImagePalette.getdata() -> tuple[str, Sequence[int] | bytes | bytearray] → method ImagePalette.getdata() -> tuple[str, bytes]
- added:
variable TYPE_CHECKING
- removed:
property Stat.sum -> list[float] - removed:
property Stat.sum2 -> list[float] - added:
property Stat.sum -> list[int] - added:
property Stat.sum2 -> list[int]
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- changed:
method PSDraw.text(xy: tuple[int, int], text: str) -> None → method PSDraw.text(xy: tuple[int, int], text: str, *, escape: bool=…) -> None
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- removed:
property SpiderImageFile.is_animated -> bool - removed:
property SpiderImageFile.n_frames -> int
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING
- added:
variable TYPE_CHECKING