pola-rs/polars · error · ModuleUpgradeRequiredError
`read_database` queries require at least
Error message
`read_database` queries require at least {module_name} version {minimum_version} What it means
Raised by `_check_module_version` when the installed database driver module's `__version__` (or `version`) is older than the minimum version polars requires to execute queries through `read_database` for that driver (typically because an Arrow-capable driver API is needed). The driver name and required minimum are included in the message. This is a deliberate gate: older drivers lack APIs polars depends on (e.g. `fetch_arrow_table`/`adbapi` batch behavior).
Solutions
- Upgrade the named driver to at least the version in the message, e.g. `pip install -U <module_name>` or `conda update <module_name>`.
- If you cannot upgrade, upgrade polars to a version whose minimum matches your driver, or downgrade polars accordingly.
- Alternatively pass the result as an already-materialized DataFrame or use `connectorx` URI mode (`read_database_uri`) which bypasses the live-driver query path.
- Check for multiple installs/shadowing (`pip show <module_name>`, `python -c "import <module>; print(<module>.__version__)"`) and clean up the environment.
Example fix
// before (requirements.txt) connectorx==0.2.6 // after (requirements.txt) connectorx>=0.3.2
Defensive patterns
Strategy: validation
Validate before calling
import importlib.metadata as md
from packaging.version import parse
def check_driver_version(module: str, minimum: str) -> None:
ver = md.version(module)
if parse(ver) < parse(minimum):
raise RuntimeError(f"{module}=={ver} < required {minimum}; run pip install -U {module}")
check_driver_version("connectorx", "0.3.2") Type guard
def has_min_version(mod: object, minimum: str) -> bool:
ver = getattr(mod, "__version__", None) or getattr(mod, "version", None)
try:
return isinstance(ver, str) and parse_version(ver) >= parse_version(minimum)
except Exception:
return False Try / catch
try:
df = pl.read_database(query, connection)
except ModuleUpgradeRequiredError as e:
print(f"Upgrade driver: pip install -U {e}")
raise Prevention
- Pin driver versions together with polars in requirements/lockfiles and upgrade them in the same change.
- Add a startup environment check that asserts driver versions meet polars' minimums.
- Use a lockfile (pip-tools/uv/conda-lock) so CI and prod get compatible driver versions.
When it happens
Trigger: Calling `pl.read_database(query, connection=conn, ...)` with an installed driver module (e.g. connectorx, adbc-driver-* , sqlalchemy, duckdb) whose parsed version is below the driver-specific minimum defined in polars' driver registry, when a query (not an SQLAlchemy-Selectable/instant DataFrame) is passed.
Common situations: Old pin in requirements.txt or conda env predating a driver upgrade; system package manager shipped an outdated driver; user upgraded polars (raising its minimum driver versions) without upgrading drivers.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- 'numpy' is required to convert numpy dtype
- pyarrow is required for converting a pandas dataframe to…
- reading delta deletion vectors requires deltalake >=
- accessing ` ` from the top-level `polars` module was…
- altair>=5.4.0 is required for `.plot`
AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18).
Data as JSON: /api/errors/840f2976e58f910e.
Report an issue: GitHub.
Appendix: source
Thrown at py-polars/src/polars/io/database/_executor.py:156
if self.can_close_cursor and hasattr(self.cursor, "close"):
from sqlalchemy.ext.asyncio.exc import AsyncContextNotStarted
with suppress(AsyncContextNotStarted):
await self.cursor.close()
@staticmethod
def _check_module_version(module_name: str, minimum_version: str) -> None:
"""Check the module version against a minimum required version."""
mod = __import__(module_name)
with suppress(AttributeError):
module_version: tuple[int, ...] | None = None
for version_attr in ("__version__", "version"):
if isinstance(ver := getattr(mod, version_attr, None), str):
module_version = parse_version(ver)
break
if module_version and module_version < parse_version(minimum_version):
msg = f"`read_database` queries require at least {module_name} version {minimum_version}"
raise ModuleUpgradeRequiredError(msg)
def _fetch_arrow(
self,
driver_properties: ArrowDriverProperties,
*,
batch_size: int | None,
iter_batches: bool,
) -> Iterable[pa.RecordBatch]:
"""Yield Arrow data as a generator of one or more RecordBatches or Tables."""
fetch_batches = driver_properties["fetch_batches"]
if not iter_batches or fetch_batches is None:
fetch_method = driver_properties["fetch_all"]
res = getattr(self.result, fetch_method)()
if isinstance(res, Iterable):
yield from res
else:
yield resView on GitHub (pinned to fe841f959e)