Fixing xarray's "Did Not Find a Match in Any of xarray's Installed Backends"
On this page
Problem statement
xr.open_dataset("file.nc") stops with a ValueError about backends: "did not find a match in any of xarray's currently installed IO backends", "found the following matches ... But their dependencies may not be installed", or "xarray is unable to open this file because it has no currently installed IO backends". The messages are about installation, and sometimes the installation is the problem. Just as often the file is not what its name says, or the path is a URL or a file object that the installed library cannot read.
The same nine files, opened in three Python environments with xarray 2026.9 β xarray alone, xarray with netCDF4, and a full geospatial environment:
- With xarray alone, the only engine was
store, and a real NetCDF file raised "found the following matches with the input file in xarray's IO backends: ['netcdf4', 'h5netcdf']. But their dependencies may not be installed". - With netCDF4 installed, an HTML error page named
.nc, a gzipped NetCDF file and a GRIB file renamed.ncall raised "did not find a match" β and still did in the full environment with six engines. - Asking for
engine="h5netcdf"in the fuller environment raisedImportError: No module named 'h5py': the engine was registered, its dependency was not installed. - An HTTPS URL raised
OSError: [Errno -90] NetCDF: file not founduntil#mode=byteswas added to it.
Quick answer
Find out which of three things is wrong:
import xarray as xr
print(list(xr.backends.list_engines())) # 1. which engines can actually load
with open("file.nc", "rb") as f:
print(f.read(8)) # 2. what the file really is
ds = xr.open_dataset("file.nc", engine="netcdf4") # 3. the precise error from one named engine
Install the library for the format β netCDF4 (or h5netcdf with h5py) for NetCDF, cfgrib for GRIB, zarr for Zarr β and pass engine= explicitly so the error comes from that engine, not from xarray's guessing.
List engines, read the message, check the file
1. List the engines that can load
xr.backends.list_engines() returns the backends whose dependencies imported. With xarray alone it returned only store; with netCDF4, netcdf4 and store; in the full environment netcdf4, h5netcdf, scipy, cfgrib, rasterio, store and zarr. If the engine you need is absent, install its library into the same environment your code runs in.
2. Read the message variant
The three messages mean different things:
- "found the following matches ... But their dependencies may not be installed" β xarray recognised the file type from its name or header and knows which engines could read it, but none is importable. Install one of those listed.
- "xarray is unable to open this file because it has no currently installed IO backends" β no engines at all, and nothing recognised the file.
- "did not find a match in any of xarray's currently installed IO backends [list]" β engines are installed, and none of them recognises the file. Suspect the file.
3. Check what the file actually is
The first bytes of a NetCDF-4 file are \x89HDF\r\n\x1a\n, of a classic NetCDF file CDF\x01 or CDF\x02, of a GRIB file GRIB, of a gzip file \x1f\x8b. A failed download saved as oisst.nc began <!DOCTYPE HTML, and every environment rejected it with "did not find a match". So did a gzipped copy and a GRIB file renamed .nc. Rename, decompress or re-download, then open with the right engine.
4. Check the dependency behind an engine
An engine can be listed while its dependency is missing: the fuller environment in Example 4 had h5netcdf installed without h5py β installed with --no-deps, the state a partial or mixed install leaves β so engine="h5netcdf" raised ImportError: No module named 'h5py', backend not available. In the minimal environments, naming an absent engine gave ModuleNotFoundError: No module named 'h5netcdf', and engine="cfgrib" gave ValueError: unrecognized engine 'cfgrib' must be one of your download engines: ['netcdf4', 'store'].
5. Match the engine to the format
scipy reads only classic NetCDF-3 and rejected NetCDF-4 with TypeError: ... is not a valid NetCDF 3 file. netcdf4 reads both. GRIB needs cfgrib, and a GRIB file with mixed levels then raised its own DatasetBuildError β see reading GRIB data. Zarr stores need the zarr package: without it, xr.open_zarr raised ImportError: The zarr package is required for working with Zarr stores.
6. Give the netCDF library a URL it understands
The netCDF C library treats a plain HTTPS URL as an OPeNDAP address. For a file on an ordinary web server, https://.../file.nc raised OSError: [Errno -90] NetCDF: file not found; https://.../file.nc#mode=bytes opened it by HTTP byte ranges. For many remote reads, download first or use a cloud-native format β see opening a NetCDF file.
7. Use the right route for file-like objects
Passing an open Python file object went to h5netcdf, the engine that reads file-likes, and failed without h5py. The netCDF4 library can open bytes in memory instead (Example 3), or install h5py and h5netcdf.
8. Rule out the environment, not just the package
"Installed" means importable by the interpreter that runs the code. Print sys.executable next to the engine list; a notebook kernel, a scheduler worker or a container image frequently runs a different environment from the terminal where the package was installed.
Diagnosing downloads that went wrong
The data used on this page
Real files, each one a way a download goes wrong. oisst.nc is one day of NOAA OISST v2.1 (1 September 2024) from NCEI; classic.nc is the same data written as classic NetCDF-3; html_error.nc is what NCEI serves for a file that does not exist, saved under a NetCDF name; oisst.nc.gz is the file gzipped; gfs_as.nc is the first message of NOAA's GFS 1Β° analysis for 10 September 2026, a GRIB file with the wrong name; oisst_truncated.nc is the first half of oisst.nc; oisst is a copy with no extension; oisst.zarr is a Zarr store of the same data. The ninth input is the NCEI URL itself.
import gzip
import os
import shutil
import requests
import xarray as xr
URL = ("https://www.ncei.noaa.gov/data/sea-surface-temperature-optimum-interpolation/"
"v2.1/access/avhrr/202409/oisst-avhrr-v02r01.20240901.nc")
if not os.path.exists("oisst.nc"):
open("oisst.nc", "wb").write(requests.get(URL, timeout=120).content)
missing = requests.get(URL.replace("20240901", "20240999"), timeout=60)
open("html_error.nc", "wb").write(missing.content)
with open("oisst.nc", "rb") as src, gzip.GzipFile("oisst.nc.gz", "wb", mtime=0) as dst: # mtime=0: the same bytes on every run
shutil.copyfileobj(src, dst)
data = open("oisst.nc", "rb").read()
open("oisst_truncated.nc", "wb").write(data[: len(data) // 2])
shutil.copy("oisst.nc", "oisst")
with xr.open_dataset("oisst.nc") as ds:
ds.to_netcdf("classic.nc", format="NETCDF3_CLASSIC")
ds.to_zarr("oisst.zarr", mode="w", zarr_format=2)
GFS = "https://noaa-gfs-bdp-pds.s3.amazonaws.com/gfs.20260910/00/atmos/gfs.t00z.pgrb2.1p00.f000"
second = int(requests.get(GFS + ".idx", timeout=60).text.splitlines()[1].split(":")[1])
open("gfs_as.nc", "wb").write(requests.get(GFS, headers={"Range": f"bytes=0-{second - 1}"}, timeout=60).content)
print({name: os.path.getsize(name) for name in ("oisst.nc", "classic.nc", "html_error.nc", "oisst.nc.gz",
"gfs_as.nc", "oisst_truncated.nc")},
f"html_error.nc came back as HTTP {missing.status_code}")
{'oisst.nc': 1532099, 'classic.nc': 8308900, 'html_error.nc': 196, 'oisst.nc.gz': 1479210, 'gfs_as.nc': 83300, 'oisst_truncated.nc': 766049} html_error.nc came back as HTTP 404
Example 1 β diagnose a file that will not open
import importlib.util
import sys
SIGNATURES = {b"\x89HDF\r\n\x1a\n": "NetCDF-4/HDF5", b"CDF\x01": "NetCDF classic", b"CDF\x02": "NetCDF 64-bit offset",
b"GRIB": "GRIB", b"\x1f\x8b": "gzip", b"PK\x03\x04": "zip", b"<": "text/HTML"}
NEEDS = {"NetCDF-4/HDF5": ["netCDF4", "h5netcdf+h5py"], "NetCDF classic": ["netCDF4", "scipy"],
"NetCDF 64-bit offset": ["netCDF4", "scipy"], "GRIB": ["cfgrib"]}
def available(requirement):
return all(importlib.util.find_spec(name) is not None for name in requirement.split("+"))
def diagnose(path):
with open(path, "rb") as f:
head = f.read(8)
kind = next((k for sig, k in SIGNATURES.items() if head.startswith(sig)), f"unknown ({head!r})")
print(f"{path}: {kind}")
for requirement in NEEDS.get(kind, []):
print(f" {requirement:14} {'installed' if available(requirement) else 'missing'}")
print(f"Python {sys.version.split()[0]}; engines that load: {list(xr.backends.list_engines())}")
for path in ("oisst.nc", "classic.nc", "html_error.nc", "oisst.nc.gz", "gfs_as.nc"):
diagnose(path)
Python 3.14.4; engines that load: ['netcdf4', 'h5netcdf', 'scipy', 'cfgrib', 'cfradial1', 'datamet', 'furuno', 'gamic', 'gini', 'hpl', 'iris', 'metek', 'nexradlevel2', 'odim', 'radolan', 'rainbow', 'rasterio', 'store', 'uf', 'zarr']
oisst.nc: NetCDF-4/HDF5
netCDF4 installed
h5netcdf+h5py installed
classic.nc: NetCDF classic
netCDF4 installed
scipy installed
html_error.nc: text/HTML
oisst.nc.gz: gzip
gfs_as.nc: GRIB
cfgrib installed
The last file is named .nc but starts with GRIB: its name was wrong, not the installation.
Example 2 β open with an explicit engine and a readable error
ENGINE_FOR = {"NetCDF-4/HDF5": "netcdf4", "NetCDF classic": "netcdf4", "NetCDF 64-bit offset": "netcdf4", "GRIB": "cfgrib"}
def open_known(path, **kwargs):
with open(path, "rb") as f:
head = f.read(8)
kind = next((k for sig, k in SIGNATURES.items() if head.startswith(sig)), None)
if kind not in ENGINE_FOR:
raise ValueError(f"{path} is not a format xarray can open directly (starts {head!r})")
return xr.open_dataset(path, engine=ENGINE_FOR[kind], **kwargs)
for path in ("oisst.nc", "html_error.nc", "oisst.nc.gz"):
try:
print(path, "->", dict(open_known(path).sizes))
except ValueError as error:
print(error)
oisst.nc -> {'time': 1, 'zlev': 1, 'lat': 720, 'lon': 1440}
html_error.nc is not a format xarray can open directly (starts b'<!DOCTYP')
oisst.nc.gz is not a format xarray can open directly (starts b'\x1f\x8b\x08\x08\x00\x00\x00\x00')
Example 3 β open NetCDF bytes without h5py
import netCDF4
with open("oisst.nc", "rb") as f:
data = f.read() # e.g. bytes from an HTTP response or object store
nc = netCDF4.Dataset("in-memory.nc", memory=data)
ds = xr.open_dataset(xr.backends.NetCDF4DataStore(nc))
print(dict(ds.sizes), float(ds["sst"].mean()))
{'time': 1, 'zlev': 1, 'lat': 720, 'lon': 1440} 14.453288078308105
The netCDF4 library opens the bytes directly, and xarray reads through the data store, so neither h5py nor a temporary file is needed.
Example 4 β the same inputs in three environments
Three environments built with uv: xarray alone; xarray with netCDF4; and a fuller one with netCDF4, SciPy, cfgrib, rioxarray and zarr, plus h5netcdf installed without its h5py dependency β the half-installed state that produces the most confusing message. The same probe runs in each.
import subprocess
import sys
ENVS = {"xarray only": ["xarray"],
"+ netCDF4": ["xarray", "netCDF4"],
"fuller": ["xarray", "netCDF4", "scipy", "cfgrib", "rioxarray", "zarr"]}
PROBE = r"""
import importlib.util, os, sys, warnings
warnings.simplefilter("ignore")
if importlib.util.find_spec("pyproj"):
import pyproj # before cfgrib's bundled PROJ; see the GRIB guide
import xarray as xr
def attempt(label, fn):
try:
fn()
print(f" {label:34} opens")
except Exception as e:
message = str(e).replace(os.getcwd() + "/", "") # keep local paths out of the output
print(f" {label:34} {type(e).__name__}: {message.splitlines()[0][:110]}")
print(f" engines: {list(xr.backends.list_engines())}")
for path in ("oisst.nc", "classic.nc", "oisst", "html_error.nc", "oisst.nc.gz", "gfs_as.nc", "oisst_truncated.nc"):
attempt(path, lambda: xr.open_dataset(path).load())
attempt("oisst.nc, engine='h5netcdf'", lambda: xr.open_dataset("oisst.nc", engine="h5netcdf"))
attempt("classic.nc, engine='scipy'", lambda: xr.open_dataset("classic.nc", engine="scipy"))
attempt("oisst.nc, engine='scipy'", lambda: xr.open_dataset("oisst.nc", engine="scipy"))
attempt("gfs_as.nc, engine='cfgrib'", lambda: xr.open_dataset("gfs_as.nc", engine="cfgrib", backend_kwargs={"indexpath": ""}))
attempt("open_zarr('oisst.zarr')", lambda: xr.open_zarr("oisst.zarr"))
attempt("open(file) object", lambda: xr.open_dataset(open("oisst.nc", "rb")))
url = sys.argv[1]
attempt("https URL", lambda: xr.open_dataset(url).load())
attempt("https URL + #mode=bytes", lambda: xr.open_dataset(url + "#mode=bytes").load())
"""
open("probe.py", "w").write(PROBE)
for name, packages in ENVS.items():
env = f"env_{name.split()[-1].lower()}"
if not os.path.exists(env):
subprocess.run(["uv", "venv", "-q", env], check=True)
subprocess.run(["uv", "pip", "install", "-q", "--python", env, *packages], check=True)
if name == "fuller":
subprocess.run(["uv", "pip", "install", "-q", "--python", env, "--no-deps", "h5netcdf"], check=True)
python = os.path.join(env, "bin", "python")
version = subprocess.run([python, "-c", "import sys, xarray; print(sys.version.split()[0], xarray.__version__)"],
capture_output=True, text=True).stdout.split()
print(f"{name} (Python {version[0]}, xarray {version[1]})")
print(subprocess.run([python, "probe.py", URL], capture_output=True, text=True).stdout, end="")
xarray only (Python 3.14.6, xarray 2026.9.0)
engines: ['store']
oisst.nc ValueError: found the following matches with the input file in xarray's IO backends: ['netcdf4', 'h5netcdf']. But their de
classic.nc ValueError: found the following matches with the input file in xarray's IO backends: ['netcdf4', 'scipy']. But their depen
oisst ValueError: found the following matches with the input file in xarray's IO backends: ['netcdf4', 'h5netcdf']. But their de
html_error.nc ValueError: xarray is unable to open this file because it has no currently installed IO backends. Xarray's read/write supp
oisst.nc.gz ValueError: xarray is unable to open this file because it has no currently installed IO backends. Xarray's read/write supp
gfs_as.nc ValueError: xarray is unable to open this file because it has no currently installed IO backends. Xarray's read/write supp
oisst_truncated.nc ValueError: found the following matches with the input file in xarray's IO backends: ['netcdf4', 'h5netcdf']. But their de
oisst.nc, engine='h5netcdf' ModuleNotFoundError: No module named 'h5netcdf'
classic.nc, engine='scipy' ModuleNotFoundError: No module named 'scipy'
oisst.nc, engine='scipy' ModuleNotFoundError: No module named 'scipy'
gfs_as.nc, engine='cfgrib' ValueError: unrecognized engine 'cfgrib' must be one of your download engines: ['store']. To install additional dependenci
open_zarr('oisst.zarr') ImportError: The zarr package is required for working with Zarr stores but could not be imported. Please install it with yo
open(file) object ValueError: found the following matches with the input file in xarray's IO backends: ['h5netcdf']. But their dependencies
https URL ValueError: found the following matches with the input file in xarray's IO backends: ['netcdf4', 'h5netcdf']. But their de
https URL + #mode=bytes ValueError: found the following matches with the input file in xarray's IO backends: ['netcdf4']. But their dependencies m
+ netCDF4 (Python 3.14.6, xarray 2026.9.0)
engines: ['netcdf4', 'store']
oisst.nc opens
classic.nc opens
oisst opens
html_error.nc ValueError: did not find a match in any of xarray's currently installed IO backends ['netcdf4']. Consider explicitly selec
oisst.nc.gz ValueError: did not find a match in any of xarray's currently installed IO backends ['netcdf4']. Consider explicitly selec
gfs_as.nc ValueError: did not find a match in any of xarray's currently installed IO backends ['netcdf4']. Consider explicitly selec
oisst_truncated.nc OSError: [Errno -101] NetCDF: HDF error: 'oisst_truncated.nc'
oisst.nc, engine='h5netcdf' ModuleNotFoundError: No module named 'h5netcdf'
classic.nc, engine='scipy' ModuleNotFoundError: No module named 'scipy'
oisst.nc, engine='scipy' ModuleNotFoundError: No module named 'scipy'
gfs_as.nc, engine='cfgrib' ValueError: unrecognized engine 'cfgrib' must be one of your download engines: ['netcdf4', 'store']. To install additional
open_zarr('oisst.zarr') ImportError: The zarr package is required for working with Zarr stores but could not be imported. Please install it with yo
open(file) object ValueError: found the following matches with the input file in xarray's IO backends: ['h5netcdf']. But their dependencies
https URL OSError: [Errno -90] NetCDF: file not found: 'https://www.ncei.noaa.gov/data/sea-surface-temperature-optimum-interpolat
https URL + #mode=bytes opens
fuller (Python 3.14.6, xarray 2026.9.0)
engines: ['netcdf4', 'h5netcdf', 'scipy', 'cfgrib', 'rasterio', 'store', 'zarr']
oisst.nc opens
classic.nc opens
oisst opens
html_error.nc ValueError: did not find a match in any of xarray's currently installed IO backends ['netcdf4', 'h5netcdf', 'scipy', 'cfgr
oisst.nc.gz ValueError: did not find a match in any of xarray's currently installed IO backends ['netcdf4', 'h5netcdf', 'scipy', 'cfgr
gfs_as.nc ValueError: did not find a match in any of xarray's currently installed IO backends ['netcdf4', 'h5netcdf', 'scipy', 'cfgr
oisst_truncated.nc OSError: [Errno -101] NetCDF: HDF error: 'oisst_truncated.nc'
oisst.nc, engine='h5netcdf' ImportError: No module named 'h5py', backend not available. Please install 'h5py' into your Python environment.
classic.nc, engine='scipy' opens
oisst.nc, engine='scipy' TypeError: Error: oisst.nc is not a valid NetCDF 3 file
gfs_as.nc, engine='cfgrib' opens
open_zarr('oisst.zarr') opens
open(file) object ImportError: No module named 'h5py', backend not available. Please install 'h5py' into your Python environment.
https URL OSError: [Errno -90] NetCDF: file not found: 'https://www.ncei.noaa.gov/data/sea-surface-temperature-optimum-interpolat
https URL + #mode=bytes opens
How xarray chooses an engine
How xarray chooses an engine
Each installed backend registers a function that inspects a path or file header and says whether it can open it. With no engine argument, xarray asks each in turn. If a backend is registered but its library cannot be imported, xarray knows the engine exists but cannot use it β the "dependencies may not be installed" variant. If every usable backend says no, the "did not find a match" variant follows.
Why the message blames installation when the file is wrong
Backend guessing only knows whether a registered engine accepts the file. An HTML page is not accepted by any, which looks the same as a NetCDF file with no NetCDF engine installed. Naming the engine skips the guess and gives the engine's own error, which is usually specific.
Why an engine can be listed and still fail
Registration and import are separate. xarray lists h5netcdf when the h5netcdf package is present; h5netcdf needs h5py at the point it opens a file. The check in the engine list can therefore pass while the open fails.
Why URLs need a mode
The netCDF C library supports several remote protocols. Without a hint it assumes OPeNDAP, a server protocol most file hosts do not speak. #mode=bytes tells it to use HTTP range requests against an ordinary file instead.
Conda and pip mixes, Zarr v3 and OPeNDAP
- Conda and pip mixes can install
netCDF4against a different HDF5 thanh5py, producing import errors only at open time. - Zarr v3 stores need a recent
zarrpackage; older versions raise errors about metadata. - OPeNDAP servers do work with plain URLs;
#mode=bytesis for plain file hosting. .nc4or no extension is fine; engines inspect headers, not only names β a copy without an extension opened normally.- Truncated downloads have a valid header and fail later with
OSError: NetCDF: HDF error. - Rasterio's engine opens NetCDF through GDAL and returns different coordinates and attributes.
xr.show_versions()prints every relevant library and version for bug reports.
Internal links
- How to open a NetCDF file in Python with xarray β engines, chunks and decoding
- Fixing open_mfdataset that is slow, hangs or will not combine β the same message from a folder of files
- How to read GRIB weather forecast data in Python β the cfgrib engine
- NetCDF and gridded data explained: dimensions, variables and attributes β NetCDF-3 and NetCDF-4
- GRIB, NetCDF or Zarr: choosing a format for gridded data β which library each format needs
- RasterioIOError: not recognized as a supported file format β the GDAL equivalent
- GDAL cannot open an S3 or HTTPS path β remote paths in GDAL
- Chunked arrays and Zarr explained β what the zarr engine reads
FAQ
What does "did not find a match in any of xarray's installed backends" mean?
xarray has engines installed but none of them recognises the file. Check the file's first bytes: an HTML error page, a gzip file or a renamed GRIB file all produced this message.
How do I install a backend for xarray?
Install the format library into the environment that runs your code: netCDF4 for NetCDF, cfgrib for GRIB, zarr for Zarr. With xarray alone, the only engine listed was store.
Why does xarray say the h5netcdf backend is not available?
The h5netcdf package is installed but h5py is not. Install h5py, or use engine="netcdf4".
Can xarray open a gzipped NetCDF file?
Not directly. Decompress it first; a .nc.gz file raised the no-match error in every environment tested.
Why does xarray fail to open a NetCDF file from a URL?
The netCDF library treated it as an OPeNDAP address. Adding #mode=bytes to the URL opened the file by HTTP byte ranges.
How do I open NetCDF data held in memory?
Pass the bytes to netCDF4.Dataset with memory=, then open it with xr.open_dataset(xr.backends.NetCDF4DataStore(nc)). This avoids needing h5py for file-like objects.