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
.zipor.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.
- Confirm the path exists with
pathlibbefore opening. - Use an absolute path and print it to remove any doubt.
- Check the real file type, not just the extension.
- Open archives through GDAL's
/vsizip/and/vsigzip/paths, or extract them. - Confirm the GDAL driver for the format is available.
- 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
Reproduce each cause, check the path, inspect the real type
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
.tifffor.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.
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.
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.
Internal links
- Introduction to Rasterio: Reading Raster Data in Python
- How to Extract Raster Values at Point Locations with Rasterio
- How to Convert a Raster to a Vector in Python
- Vector vs Raster Data in Python GIS: Key Differences
- GeoPandas Not Reading Shapefile: Common Causes and Fixes
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.