{"record":{"id":"f1d524b3fe6f790a","repo":"pola-rs/polars","slug":"a-more-recent-version-of-fastexcel-is-required-f","errorCode":null,"errorMessage":"a more recent version of `fastexcel` is required for 'schema_sample_rows' (>= 0.9; found {original_version})","messagePattern":"a more recent version of `fastexcel` is required for 'schema_sample_rows' \\(>= 0\\.9; found (.+?)\\)","errorType":"exception","errorClass":"ModuleUpgradeRequiredError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/spreadsheet/functions.py","lineNumber":1048,"sourceCode":"    parser: Any,\n    *,\n    sheet_name: str | None,\n    read_options: dict[str, Any],\n    schema_overrides: SchemaDict | None,\n    columns: Sequence[int] | Sequence[str] | None,\n    table_name: str | None = None,\n    drop_empty_rows: bool,\n    drop_empty_cols: bool,\n    raise_if_empty: bool,\n) -> pl.DataFrame:\n    # if we have 'schema_overrides' and a more recent version of `fastexcel`\n    # we can pass translated dtypes to the engine to refine the initial parse\n    fastexcel = import_optional(\"fastexcel\")\n    fastexcel_version = parse_version(original_version := fastexcel.__version__)\n\n    if fastexcel_version < (0, 9) and \"schema_sample_rows\" in read_options:\n        msg = f\"a more recent version of `fastexcel` is required for 'schema_sample_rows' (>= 0.9; found {original_version})\"\n        raise ModuleUpgradeRequiredError(msg)\n    if fastexcel_version < (0, 10, 2) and \"use_columns\" in read_options:\n        msg = f\"a more recent version of `fastexcel` is required for 'use_columns' (>= 0.10.2; found {original_version})\"\n        raise ModuleUpgradeRequiredError(msg)\n    if table_name and fastexcel_version < (0, 12):\n        msg = f\"a more recent version of `fastexcel` is required for 'table_name' (>= 0.12.0; found {original_version})\"\n        raise ValueError(msg)\n\n    if columns:\n        if not isinstance(columns, list):\n            columns = list(columns)  # type: ignore[assignment]\n        read_options[\"use_columns\"] = columns\n\n    schema_overrides = schema_overrides or {}\n    if read_options.get(\"schema_sample_rows\") == 0:\n        # ref: https://github.com/ToucanToco/fastexcel/issues/236\n        del read_options[\"schema_sample_rows\"]\n        read_options[\"dtypes\"] = (\n            \"string\"","sourceCodeStart":1030,"sourceCodeEnd":1066,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/spreadsheet/functions.py#L1030-L1066","documentation":"Raised as ModuleUpgradeRequiredError in _read_spreadsheet_calamine when read_options contains 'schema_sample_rows' and the installed fastexcel is older than 0.9.0. Crucially, for the calamine engine _get_read_options ALWAYS injects read_options['schema_sample_rows'] = infer_schema_length, so with fastexcel 0.7.x/0.8.x (which pass the 0.7.0 import floor) essentially EVERY read_excel/read_ods call with engine='calamine' hits this — read_ods is affected too because it hardcodes calamine.","triggerScenarios":"Any pl.read_excel(src) or pl.read_ods(src) with fastexcel 0.7.x or 0.8.x installed; explicitly passing infer_schema_length or read_options={'schema_sample_rows': n} makes the trigger obvious but is not required.","commonSituations":"A stale fastexcel pin in requirements/lockfile while polars was upgraded (or vice versa); shared Docker images where the floor version 0.7.0 was pinned deliberately long ago; read_ods users, since there is no alternate ODS engine.","solutions":["Upgrade fastexcel to at least 0.9 (recommended: latest, which also unlocks use_columns and table_name)","If you cannot upgrade, use engine='xlsx2csv' or 'openpyxl' for .xlsx inputs","For ODS inputs there is no alternative engine — upgrading fastexcel is the only fix"],"exampleFix":"# before: fastexcel 0.7.x pinned -> every calamine read raises\n# requirements.txt: fastexcel==0.7.6\n\n# after\npip install -U 'fastexcel>=0.12'\n# requirements.txt: fastexcel>=0.12","handlingStrategy":"validation","validationCode":"def fastexcel_version() -> tuple[int, ...]:\n    import fastexcel\n    return tuple(int(p) for p in fastexcel.__version__.split('.')[:3])\n\nver = fastexcel_version()\nif ver < (0, 9):\n    # schema_sample_rows is ALWAYS injected for calamine, so downgrade expectations\n    df = pl.read_excel(src, engine='openpyxl')  # or 'xlsx2csv' for .xlsx\nelse:\n    df = pl.read_excel(src)  # calamine default","typeGuard":null,"tryCatchPattern":"from polars.exceptions import ModuleUpgradeRequiredError\ntry:\n    df = pl.read_excel(src)  # engine='calamine' default; read_ods too\nexcept ModuleUpgradeRequiredError as e:\n    if \"'schema_sample_rows'\" in str(e):\n        raise RuntimeError('fastexcel>=0.9 required: pip install -U fastexcel') from e\n    raise","preventionTips":["Pin fastexcel >= 0.9 (preferably latest) whenever polars is present — the calamine path always injects schema_sample_rows","read_ods has no alternate engine, so ODS pipelines must keep fastexcel current","Add a startup version gate: assert fastexcel.__version__ >= 0.9 in your app bootstrap"],"tags":["polars","excel","ods","calamine","fastexcel","version","upgrade"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}