{"record":{"id":"840f2976e58f910e","repo":"pola-rs/polars","slug":"read-database-queries-require-at-least-module-n","errorCode":null,"errorMessage":"`read_database` queries require at least {module_name} version {minimum_version}","messagePattern":"`read_database` queries require at least (.+?) version (.+?)","errorType":"exception","errorClass":"ModuleUpgradeRequiredError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/database/_executor.py","lineNumber":156,"sourceCode":"        if self.can_close_cursor and hasattr(self.cursor, \"close\"):\n            from sqlalchemy.ext.asyncio.exc import AsyncContextNotStarted\n\n            with suppress(AsyncContextNotStarted):\n                await self.cursor.close()\n\n    @staticmethod\n    def _check_module_version(module_name: str, minimum_version: str) -> None:\n        \"\"\"Check the module version against a minimum required version.\"\"\"\n        mod = __import__(module_name)\n        with suppress(AttributeError):\n            module_version: tuple[int, ...] | None = None\n            for version_attr in (\"__version__\", \"version\"):\n                if isinstance(ver := getattr(mod, version_attr, None), str):\n                    module_version = parse_version(ver)\n                    break\n            if module_version and module_version < parse_version(minimum_version):\n                msg = f\"`read_database` queries require at least {module_name} version {minimum_version}\"\n                raise ModuleUpgradeRequiredError(msg)\n\n    def _fetch_arrow(\n        self,\n        driver_properties: ArrowDriverProperties,\n        *,\n        batch_size: int | None,\n        iter_batches: bool,\n    ) -> Iterable[pa.RecordBatch]:\n        \"\"\"Yield Arrow data as a generator of one or more RecordBatches or Tables.\"\"\"\n        fetch_batches = driver_properties[\"fetch_batches\"]\n        if not iter_batches or fetch_batches is None:\n            fetch_method = driver_properties[\"fetch_all\"]\n            res = getattr(self.result, fetch_method)()\n\n            if isinstance(res, Iterable):\n                yield from res\n            else:\n                yield res","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/pola-rs/polars/blob/fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e/py-polars/src/polars/io/database/_executor.py#L138-L174","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (requirements.txt)\nconnectorx==0.2.6\n// after (requirements.txt)\nconnectorx>=0.3.2","handlingStrategy":"validation","validationCode":"import importlib.metadata as md\nfrom packaging.version import parse\n\ndef check_driver_version(module: str, minimum: str) -> None:\n    ver = md.version(module)\n    if parse(ver) < parse(minimum):\n        raise RuntimeError(f\"{module}=={ver} < required {minimum}; run pip install -U {module}\")\n\ncheck_driver_version(\"connectorx\", \"0.3.2\")","typeGuard":"def has_min_version(mod: object, minimum: str) -> bool:\n    ver = getattr(mod, \"__version__\", None) or getattr(mod, \"version\", None)\n    try:\n        return isinstance(ver, str) and parse_version(ver) >= parse_version(minimum)\n    except Exception:\n        return False","tryCatchPattern":"try:\n    df = pl.read_database(query, connection)\nexcept ModuleUpgradeRequiredError as e:\n    print(f\"Upgrade driver: pip install -U {e}\")\n    raise","preventionTips":["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."],"tags":["polars","version-compatibility","dependency","python"],"backgroundTag":"missing-dependency","analyzedSha":"fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e","analyzedAt":"2026-09-18T22:14:11.667Z","contentChangedAt":"2026-09-18T22:14:11.667Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}