RasterioIOError: Not Recognized as a Supported File Format (How to Fix)

On this page

Problem statement

rasterio.open() stops on the first line with one of these:

rasterio.errors.RasterioIOError: 'data/download.tif' not recognized as being in a supported file format.

or:

rasterio.errors.RasterioIOError: data/dem.tiff: No such file or directory

Nothing has been read yet, so the message is all you have. It turns up when:

  • the path looks correct, but Python cannot find the file
  • the file exists, but Rasterio refuses to open it
  • the same file opens in QGIS but fails in Python
  • a download finished, but the file will not open

The causes, in roughly the order to check them:

  • a wrong or relative path, so the file is not found
  • the file is actually a different format than its extension suggests
  • a corrupted or partial download
  • an unextracted container such as a .zip or .tif.gz
  • a container format whose data sits in subdatasets
  • a GDAL driver that is not available in your environment

The goal is to identify which category the problem belongs to and apply the right fix quickly. Every message on this page was produced by running the code below against a real file β€” a window of the Copernicus 30 m DEM over Rutland β€” and deliberately broken copies of it.

Quick answer

Most RasterioIOError failures are fixed by checking the path first, then opening the raster inside a context manager.

  1. Confirm the path exists with pathlib before opening.
  2. Use an absolute path and print it to remove any doubt.
  3. Check the real file type, not just the extension.
  4. Open archives through GDAL's /vsizip/ and /vsigzip/ paths, or extract them.
  5. Confirm the GDAL driver for the format is available.
  6. Re-download the file if it is corrupted or incomplete.
import rasterio
from pathlib import Path

raster_path = Path("data/dem.tif").resolve()
print("Opening:", raster_path.relative_to(Path.cwd()))
print("Exists:", raster_path.exists())

if not raster_path.exists():
    raise FileNotFoundError(raster_path)

with rasterio.open(raster_path) as src:
    print("Driver:", src.driver)
    print("Band count:", src.count)
    band1 = src.read(1)
    print("Band 1 shape:", band1.shape)
Opening: data/dem.tif
Exists: True
Driver: GTiff
Band count: 1
Band 1 shape: (900, 1008)

This confirms the file is present, then opens it with Rasterio and reports the driver that GDAL used.

Diagnosis flow

RasterioIOError: diagnosisDoes the path exist?Fix the path or the workingdirectoryNoYesHeader bytes match a format?ZIP β†’ /vsizip/; gzip β†’ unzip;HTML β†’ re-downloadNoYesGDAL driver available? Reinstall GDALspatialworkflow.io
Diagnosing a RasterioIOError from the path and file header.

Reproduce each cause, check the path, inspect the real type

Causes of RasterioIOError "not recognized as a supported file format". what the error really meansCauseHow to confirm and fixThe path does not existPath(p).exists() β€” check spellingand cwdThe extension lies about thecontentRead the first bytes; it may beHTML or a zipThe file is inside an archiveOpen with zip:// or /vsizip/ pathsGDAL has no driver for that formatList drivers; install a fullerGDAL build spatialworkflow.io
The first two account for most cases, and neither is really about rasters.

Reproduce the error with each cause

The quickest way to learn what each message means is to produce them. The setup made one good file and five bad ones:

from rasterio.errors import RasterioIOError

CASES = {
    "the real GeoTIFF": "data/dem.tif",
    "wrong extension in the path": "data/dem.tiff",
    "a server error page saved as .tif": "data/download.tif",
    "a ZIP renamed .tif": "data/zipped.tif",
    "a gzipped GeoTIFF": "data/dem.tif.gz",
    "the first 30% of a download": "data/partial.tif",
}
for label, path in CASES.items():
    try:
        with rasterio.open(path) as src:
            src.read(1)
            print(f"{label:34} opened: {src.driver}, {src.width} x {src.height}")
    except RasterioIOError as exc:
        print(f"{label:34} RasterioIOError: {str(exc)[:80]}")
the real GeoTIFF                   opened: GTiff, 1008 x 900
wrong extension in the path        RasterioIOError: data/dem.tiff: No such file or directory
a server error page saved as .tif  RasterioIOError: 'data/download.tif' not recognized as being in a supported file format.
a ZIP renamed .tif                 RasterioIOError: 'data/zipped.tif' not recognized as being in a supported file format.
a gzipped GeoTIFF                  RasterioIOError: 'data/dem.tif.gz' not recognized as being in a supported file format.
the first 30% of a download        RasterioIOError: Read failed. See previous exception for details.

Three different messages for five failures, and the truncated download is the one to notice: it did not fail at open() at all. GDAL read the header, which was intact, and only reading the pixels found the missing tiles β€” so a check that only opens files will pass a broken download.

Verify the path exists with pathlib

Many RasterioIOError cases are simple path problems. The "No such file or directory" variant almost always means the file is not where the code is looking.

for name in ("data/dem.tif", "data/dem.tiff"):
    raster_path = Path(name).resolve()
    print(f"{name:14} exists: {raster_path.exists()!s:5} is file: {raster_path.is_file()!s:5} "
          f"size: {raster_path.stat().st_size if raster_path.exists() else 'missing'}")
data/dem.tif   exists: True  is file: True  size: 3144211
data/dem.tiff  exists: False is file: False size: missing

When the path is the problem:

  • wrong working directory, so a relative path resolves to the wrong place
  • a typo in the filename β€” including .tiff for .tif
  • the file renamed or moved
  • Windows backslash escaping problems

If exists() returns False, fix the path before doing anything else.

Inspect the real file type, not the extension

A file named .tif is not always a GeoTIFF. Servers answer a bad request with an HTML or XML error page, portals hand out ZIP archives, and a script that saves the response under the expected name turns any of them into a .tif. Reading the first bytes reveals the real type.

SIGNATURES = {b"II*\x00": "TIFF, little-endian", b"MM\x00*": "TIFF, big-endian", b"II+\x00": "BigTIFF",
              b"PK\x03\x04": "ZIP archive", b"\x1f\x8b": "gzip", b"<!DOCTYP": "HTML page", b"<html": "HTML page",
              b"<?xml": "XML document", b"\x89HDF": "HDF5 / NetCDF-4", b"CDF\x01": "NetCDF-3"}

def sniff(path):
    header = Path(path).read_bytes()[:8]
    kind = next((name for sig, name in SIGNATURES.items() if header.startswith(sig)), "unknown")
    return header, kind

for path in ("data/dem.tif", "data/download.tif", "data/zipped.tif", "data/dem.tif.gz", "data/partial.tif"):
    header, kind = sniff(path)
    print(f"{path:18} {str(header):36} {kind}")
data/dem.tif       b'II*\x00\x08\x00\x00\x00'           TIFF, little-endian
data/download.tif  b'<?xml ve'                          XML document
data/zipped.tif    b'PK\x03\x04\x14\x00\x00\x00'        ZIP archive
data/dem.tif.gz    b'\x1f\x8b\x08\x00\x00\x00\x00\x00'  gzip
data/partial.tif   b'II*\x00\x08\x00\x00\x00'           TIFF, little-endian

If the header does not match the format you expected, the file is the problem, not Rasterio. The partial download shows the limit of this check: its header is a perfect TIFF header, because the first bytes are exactly the ones that did arrive. Compare the size with the source as well.

Whether rasterio could open, and then read, a real GeoTIFF and five broken copies of it. opening is not the same as readingrasterio.open()src.read(1)the real GeoTIFFβœ“openedβœ“1008 Γ— 900path says .tiffβœ•No such fileβ€”server error pageβœ•not recognizedβ€”ZIP named .tifβœ•not recognizedβ€”gzipped GeoTIFFβœ•not recognizedβ€”first 30% only!openedβœ•Read failedThe truncated download is the dangerous one: its header is intact, so it opens spatialworkflow.io
The run above, cause by cause: only one failure waited until the pixels were read.

List available GDAL drivers

Rasterio reads formats through GDAL drivers. If the driver for your format is not built into your GDAL, the file will not open even when it is valid.

with rasterio.Env() as env:
    drivers = env.drivers()
print(f"rasterio {rasterio.__version__}, GDAL {rasterio.__gdal_version__}: {len(drivers)} drivers")
print({name: name in drivers for name in ("GTiff", "COG", "JPEG", "PNG", "netCDF", "HDF5", "JP2OpenJPEG", "ECW")})
rasterio 1.5.1, GDAL 3.12.4: 147 drivers
{'GTiff': True, 'COG': True, 'JPEG': True, 'PNG': True, 'netCDF': True, 'HDF5': True, 'JP2OpenJPEG': True, 'ECW': False}

If a common driver such as GTiff is missing, the GDAL build is incomplete and the environment needs to be reinstalled. The pip wheel used here bundles its own GDAL with a broad set of drivers β€” though not proprietary ones such as ECW, which need a GDAL built with the vendor's SDK.

Handle zipped or compressed rasters

If the file is a ZIP archive or a .tif.gz, Rasterio cannot open it as a plain raster. You have two options: extract it first, or point Rasterio at the file inside the archive using a GDAL virtual path.

import zipfile

with zipfile.ZipFile("data/dem.zip") as z:
    print("inside the ZIP:", z.namelist())
    z.extractall("data/dem_extracted")
with rasterio.open("data/dem_extracted/dem.tif") as src:
    print("extracted:        ", src.driver, src.shape)

for virtual in ("/vsizip/data/dem.zip/dem.tif", "zip+file://data/dem.zip!dem.tif", "/vsigzip/data/dem.tif.gz"):
    with rasterio.open(virtual) as src:
        print(f"{virtual:34}", src.driver, src.shape)
inside the ZIP: ['dem.tif']
extracted:         GTiff (900, 1008)
/vsizip/data/dem.zip/dem.tif       GTiff (900, 1008)
zip+file://data/dem.zip!dem.tif    GTiff (900, 1008)
/vsigzip/data/dem.tif.gz           GTiff (900, 1008)

The /vsizip/ prefix tells GDAL to look inside the archive without extracting it to disk; zip+file://…! is Rasterio's own spelling of the same thing, and /vsigzip/ does it for a single gzipped file.

Opening with error handling that names the cause

The data used on this page

A window of the Copernicus GLO-30 DEM over Rutland (Β© DLR e.V. 2010–2014 and Β© Airbus Defence and Space GmbH 2014–2018, provided under COPERNICUS by the European Union and ESA), read from its cloud-optimised GeoTIFF on AWS. The setup then breaks copies of it the ways real downloads break.

import gzip
import shutil
import zipfile
from pathlib import Path

import numpy as np
import rasterio
import requests
from rasterio.windows import from_bounds

Path("data").mkdir(exist_ok=True)
DEM_URL = ("https://copernicus-dem-30m.s3.amazonaws.com/Copernicus_DSM_COG_10_N52_00_W001_00_DEM/"
           "Copernicus_DSM_COG_10_N52_00_W001_00_DEM.tif")
if not Path("data/dem.tif").exists():
    with rasterio.open(DEM_URL) as src:
        window = from_bounds(-0.84, 52.52, -0.42, 52.77, src.transform)
        profile = src.profile | {"height": int(round(window.height)), "width": int(round(window.width)),
                                 "transform": src.window_transform(window)}
        with rasterio.open("data/dem.tif", "w", **profile) as dst:
            dst.write(src.read(window=window))

# the ways downloads break
missing = requests.get(DEM_URL.replace("N52_00_W001", "N52_00_W999"), timeout=60)
Path("data/download.tif").write_bytes(missing.content)                     # the server's error page
with zipfile.ZipFile("data/dem.zip", "w") as z:
    z.write("data/dem.tif", "dem.tif")
shutil.copy("data/dem.zip", "data/zipped.tif")                              # an archive with the wrong name
Path("data/dem.tif.gz").write_bytes(gzip.compress(Path("data/dem.tif").read_bytes()))
good = Path("data/dem.tif").read_bytes()
Path("data/partial.tif").write_bytes(good[: int(len(good) * 0.3)])       # a download cut off at 30%
print("DEM:", len(good), "bytes | the missing tile's server answered", missing.status_code,
      missing.headers.get("Content-Type"))
DEM: 3144211 bytes | the missing tile's server answered 404 application/xml

Example 1: Open with error handling and report the cause

def open_or_explain(raster_path):
    path = Path(raster_path)
    try:
        with rasterio.open(path) as src:
            src.read(1, window=((src.height - 1, src.height), (0, src.width)))   # touch the last row too
            return f"opened: {src.driver}, {src.width} x {src.height}"
    except rasterio.errors.RasterioIOError as exc:
        if not path.exists():
            return "no such file at this path"
        header, kind = sniff(path)
        if kind != "TIFF, little-endian":
            return f"not a TIFF at all: the file is a {kind}"
        return f"a TIFF that cannot be read to the end - re-download it ({path.stat().st_size:,} bytes)"


for path in CASES.values():
    print(f"{path:18} {open_or_explain(path)}")
data/dem.tif       opened: GTiff, 1008 x 900
data/dem.tiff      no such file at this path
data/download.tif  not a TIFF at all: the file is a XML document
data/zipped.tif    not a TIFF at all: the file is a ZIP archive
data/dem.tif.gz    not a TIFF at all: the file is a gzip
data/partial.tif   a TIFF that cannot be read to the end - re-download it (943,263 bytes)

Reading the last row is what catches the truncated file: the header opens, and the end of the image is where the missing bytes were.

Example 2: Read straight from a URL, and what a bad URL says

for url in (DEM_URL, DEM_URL.replace("N52_00_W001", "N52_00_W999")):
    try:
        with rasterio.open(url) as src:
            print("opened over HTTP:", src.driver, src.width, "x", src.height, "| COG layout:",
                  src.tags(ns="IMAGE_STRUCTURE").get("LAYOUT"))
    except rasterio.errors.RasterioIOError as exc:
        print("RasterioIOError:", str(exc)[:110])
opened over HTTP: GTiff 2400 x 3600 | COG layout: COG
RasterioIOError: HTTP response code: 404

Rasterio passes an https:// path to GDAL's /vsicurl/ handler, so a cloud-optimised GeoTIFF opens without a download. A URL that points at nothing does not produce "No such file or directory" but GDAL's HTTP status β€” worth knowing when the error appears in a job that used to work.

Example 3: Containers with subdatasets

NetCDF and HDF files often hold several variables. Here one is written from the DEM with two of them:

import xarray as xr

with rasterio.open("data/dem.tif") as src:
    elevation = src.read(1)
xr.Dataset({"elevation": (("y", "x"), elevation),
            "relief": (("y", "x"), elevation - float(np.median(elevation)))}).to_netcdf("data/terrain.nc")

with rasterio.open("data/terrain.nc") as src:
    print("container: driver", src.driver, "| bands:", src.count, "| subdatasets:", src.subdatasets)
with rasterio.open('netcdf:data/terrain.nc:elevation') as src:
    print("one variable: driver", src.driver, "| bands:", src.count, "| shape:", src.shape)
container: driver netCDF | bands: 0 | subdatasets: ['netcdf:"data/terrain.nc":elevation', 'netcdf:"data/terrain.nc":relief']
one variable: driver netCDF | bands: 1 | shape: (900, 1008)

The container opens, with no bands of its own; the data is in the subdatasets it lists, and each one opens by the name GDAL gives it.

Example 4: Check whether a format's driver is available before opening

def open_with(path, required_driver):
    with rasterio.Env() as env:
        if required_driver not in env.drivers():
            return f"{required_driver} driver is not available in this GDAL build"
    with rasterio.open(path) as src:
        return f"opened with {src.driver}"


print(open_with("data/dem.tif", "GTiff"))
print(open_with("data/dem.ecw", "ECW"))
opened with GTiff
ECW driver is not available in this GDAL build

GDAL chooses a driver by content

Rasterio does not parse raster files itself. It hands the path to GDAL, which tries each registered driver in turn until one recognises the data. The driver is chosen by content, not only by extension. When no driver claims the file, GDAL reports that it is "not recognized as being in a supported file format," and Rasterio raises RasterioIOError.

Identifying what a file really is from its first bytes. the extension is a claim, not a factfirst bytesthe file really isopen it withII* / MM*a TIFF / GeoTIFFrasterio.open()PK\x03\x04a zip archive/vsizip/ path\x1f\x8bgzip/vsigzip/ path<html / <?xmlan error pagethe download failed{ or [JSONnot a raster at allThe "GeoTIFF" on this page that starts with <?xml is S3's 404 answer, saved under the expected nameTwo lines reading the header settle the question immediately spatialworkflow.io
Downloads that silently failed are one of the most common causes of this error.

This means the error has two broad causes. Either GDAL never received valid bytes, or it received bytes that no available driver understands.

The first cause covers missing files, wrong paths, partial downloads, and unextracted archives. In those cases the file is absent, truncated, or is really a container such as a ZIP. The "No such file or directory" message is the clearest sign of this group β€” and a truncated file is the treacherous member of it, because its header is intact and it opens.

The second cause covers genuine format and driver problems. The file might be a format whose driver is not in your GDAL build, or it might be corrupted so that no driver matches its header. Checking src.driver on files that do open, and listing the drivers in your environment, helps separate these cases.

Because format detection is content-based, the file extension is only a hint. The server's error document above β€” S3's XML answer to a missing tile, saved under a .tif name β€” was rejected; the ZIP renamed to .tif was rejected too, because GDAL does not look inside an archive unless the path says to.

Relative paths, partial downloads and extensions

Relative vs absolute paths

A relative path like data/dem.tif depends on the current working directory, which can differ between your editor, a notebook, and a scheduled job. Resolve to an absolute path with Path(...).resolve() and print it before opening.

Partial or corrupted downloads

A download that stops early leaves a file with the right name, the right extension and β€” as the truncated copy showed β€” a valid header. The size is smaller than the source's, and reading the image through to the end fails. Compare the size with the Content-Length the server sent, and re-download.

.tif vs .tiff

Both extensions map to the same GeoTIFF driver in GDAL, so the difference does not cause this error by itself. It causes the path error when the code says one and the disk has the other.

Windows path separators

On Windows, backslashes in normal strings are escape characters, so "C:\new\dem.tif" can break. Use a raw string r"C:\data\dem.tif", forward slashes, or a pathlib.Path. GDAL virtual paths like /vsizip/ always use forward slashes regardless of platform.

FAQ

Why does Rasterio say my file is not a supported format when it opens in QGIS?

QGIS and Rasterio both use GDAL, so this usually points to a different path, a partial copy, or a GDAL build that lacks the driver. Confirm the path exists, check the file size against the source, and list your available drivers.

How do I open a GeoTIFF that is inside a ZIP without extracting it?

Use a GDAL virtual path with the /vsizip/ prefix, such as rasterio.open("/vsizip/data/dem.zip/dem.tif"). This reads the raster directly from the archive.

Does the file extension determine how Rasterio reads a raster?

No. GDAL identifies the format from the file's content, trying each driver until one matches. The extension is only a hint, so a mislabeled or corrupted file still fails.

What does "No such file or directory" mean if the file is right there?

It almost always means the path your code uses does not point to the file, often because of a relative path or wrong working directory. Print Path(raster_path).resolve() and check exists() to confirm.

How do I read a Cloud-Optimized GeoTIFF directly from a URL with Rasterio?

Prefix the HTTPS address with /vsicurl/, for example rasterio.open("/vsicurl/https://example.com/dem.tif"). The COG format lets GDAL fetch only the needed byte ranges, but the request fails if the URL is wrong or the server returns an error page.

Why does rasterio.open() fail on a NetCDF or HDF5 file with multiple subdatasets?

Such files expose their data through subdatasets rather than at the top level, so opening the container directly often fails or returns no bands. List src.subdatasets (or use the NETCDF:"file.nc":varname path syntax) and open the specific subdataset you need.

How do I check whether my file is truly a valid GeoTIFF before opening it?

Read the first bytes and confirm the TIFF signature: b"II*\x00" (little-endian) or b"MM\x00*" (big-endian). If you instead see b"PK\x03\x04" it is a ZIP, and b"<!DOCTYP" means an HTML error page was downloaded in place of the raster.

Why does a JPEG or PNG open in an image viewer but fail in Rasterio?

Plain JPEG and PNG need the matching GDAL drivers, and a build missing them will refuse the file even though it is valid. List the available drivers with rasterio.Env().drivers(); if the driver is absent, reinstall GDAL from conda-forge, which ships a fuller driver set.