pola-rs/polars · error · NotImplementedError
unrecognized engine
Error message
unrecognized engine: {engine!r} What it means
Polars' `read_excel`/spreadsheet reader only supports a fixed set of engines ('calamine', 'openpyxl', 'xlsx2csv', 'pyxlsb', 'ods'). After dispatching through the engine-specific reader functions, any engine value that did not match falls through to this final guard in `_initialise_spreadsheet_parser`, which raises `NotImplementedError`. It means the requested engine is not a known/supported backend name.
Solutions
- Set engine to one of the supported values: 'calamine' (default), 'openpyxl', 'xlsx2csv', 'pyxlsb', or 'ods'.
- Check spelling/case of the engine string exactly as listed in the polars docs.
- If the engine value comes from config, validate it against the supported set before calling read_excel.
- Install the dependency required for the chosen engine (e.g. pip install fastexcel for 'calamine').
Example fix
// before
pl.read_excel('data.xlsx', engine='xlsxwriter')
// after
pl.read_excel('data.xlsx', engine='calamine') # or 'openpyxl', 'xlsx2csv', 'pyxlsb', 'ods' Defensive patterns
Strategy: validation
Validate before calling
SUPPORTED_ENGINES = {'calamine', 'openpyxl', 'xlsx2csv', 'pyxlsb', 'ods'}
if engine not in SUPPORTED_ENGINES:
raise ValueError(f'engine must be one of {sorted(SUPPORTED_ENGINES)}, got {engine!r}') Type guard
def is_valid_engine(engine: object) -> TypeGuard[str]:
return isinstance(engine, str) and engine in {'calamine', 'openpyxl', 'xlsx2csv', 'pyxlsb', 'ods'} Try / catch
try:
df = pl.read_excel(path, engine=engine)
except NotImplementedError as e:
log.error('Unsupported spreadsheet engine: %s', e)
df = pl.read_excel(path, engine='calamine') Prevention
- Keep engine names in a constant/set sourced from the docs
- Validate config-provided engine values at startup
- Prefer the default 'calamine' engine unless you need a specific backend
When it happens
Trigger: Calling `pl.read_excel(..., engine='xlsxwriter')`, a misspelled engine like `'openpyl'`, or passing a non-string value; also occurs when a custom string is passed to `read_ods`/`read_xls` wrappers that share `_read_spreadsheet`.
Common situations: Typo in the engine name; copying example code from another library (e.g. pandas' 'openpyxl' vs polars' engine list); passing a config value read from a YAML/JSON file that holds an unsupported engine string; confusion between writer engines (xlsxwriter) and reader engines.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- cannot create a second
- cannot specify both `columns` and `read_options["columns"]`
- cannot specify both `columns` and…
- cannot specify both `infer_schema_length` and…
- cannot specify both `infer_schema_length` and…
AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18).
Data as JSON: /api/errors/c2e40f7b5809540f.
Report an issue: GitHub.
Appendix: source
Thrown at py-polars/src/polars/io/spreadsheet/functions.py:902
if reading_bytesio:
source = source.getvalue() # type: ignore[union-attr]
elif isinstance(source, (BufferedReader, TextIOWrapper)):
if "b" not in source.mode:
msg = f"file {source.name!r} must be opened in binary mode"
raise OSError(msg)
elif (filename := source.name) and Path(filename).exists():
source = filename
else:
source = source.read()
parser = fastexcel.read_excel(source, **engine_options)
sheets = [
{"index": i + 1, "name": nm} for i, nm in enumerate(parser.sheet_names)
]
return _read_spreadsheet_calamine, parser, sheets
msg = f"unrecognized engine: {engine!r}"
raise NotImplementedError(msg)
def _csv_buffer_to_frame(
csv: StringIO,
*,
separator: str,
read_options: dict[str, Any],
schema_overrides: SchemaDict | None,
drop_empty_rows: bool,
drop_empty_cols: bool,
raise_if_empty: bool,
) -> pl.DataFrame:
"""Translate StringIO buffer containing delimited data as a DataFrame."""
# handle (completely) empty sheet data
if csv.tell() == 0:
return _empty_frame(raise_if_empty)
# otherwise rewind the buffer and parse as csvView on GitHub (pinned to fe841f959e)