pola-rs/polars · error · ParameterCollisionError
the values of `has_header` and `read_options["has_header"]`
Error message
the values of `has_header` and `read_options["has_header"]` are not compatible
What it means
Raised by polars.read_excel (engine='xlsx2csv') in _get_read_options when `has_header` is set in read_options AND its value fails the identity test `read_options['has_header'] is not has_header`. Because the check is `is not` (not !=), it fires both for genuinely conflicting values (True vs False) and for values that are merely equal-but-not-identical, e.g. the int 0/1 or numpy.bool_(False) versus the Python bool False. read_options['has_header'] is passed through to the internal read_csv call, so polars needs one unambiguous value.
Source
Thrown at py-polars/src/polars/io/spreadsheet/functions.py:763
infer_schema_length != N_INFER_DEFAULT
):
msg = 'cannot specify both `infer_schema_length` and `read_options["schema_sample_rows"]`'
raise ParameterCollisionError(msg)
read_options["schema_sample_rows"] = infer_schema_length
if has_header is False and "header_row" not in read_options:
read_options["header_row"] = None
elif engine == "xlsx2csv":
if ("columns" in read_options) and columns:
msg = 'cannot specify both `columns` and `read_options["columns"]`'
raise ParameterCollisionError(msg)
elif (
"has_header" in read_options
and read_options["has_header"] is not has_header
):
msg = 'the values of `has_header` and `read_options["has_header"]` are not compatible'
raise ParameterCollisionError(msg)
elif ("infer_schema_length" in read_options) and (
infer_schema_length != N_INFER_DEFAULT
):
msg = 'cannot specify both `infer_schema_length` and `read_options["infer_schema_length"]`'
raise ParameterCollisionError(msg)
read_options["infer_schema_length"] = infer_schema_length
if "has_header" not in read_options:
read_options["has_header"] = has_header
else:
read_options["infer_schema_length"] = infer_schema_length
read_options["has_header"] = has_header
return read_options
def _get_sheet_names(
sheet_id: int | Sequence[int] | None,View on GitHub (pinned to df599052da)
Solutions
- Set has_header in exactly one place: prefer the top-level pl.read_excel(..., has_header=...) parameter and remove it from read_options
- If it must live in read_options, coerce to a real Python bool: read_options={'has_header': bool(value)} and leave the top-level at default
- Never pass ints or numpy bools as has_header values
Example fix
# before (cfg.header is 0/1 from JSON)
pl.read_excel(src, engine='xlsx2csv', read_options={'has_header': cfg.header})
# after
pl.read_excel(src, engine='xlsx2csv', has_header=bool(cfg.header)) Defensive patterns
Strategy: validation
Validate before calling
has_header = bool(cfg.get('header', True)) # coerce ints/np.bool_ from config
opts = dict(read_options or {})
opts.pop('has_header', None) # keep the value in exactly one place
df = pl.read_excel(src, engine='xlsx2csv', has_header=has_header, read_options=opts) Type guard
def is_pure_bool(v) -> bool:
"""True only for real Python bools (the engine compares with `is`)."""
return v is True or v is False Try / catch
from polars.exceptions import ParameterCollisionError
try:
df = pl.read_excel(src, engine='xlsx2csv', read_options=opts)
except ParameterCollisionError as e:
if 'has_header' in str(e):
opts = {k: v for k, v in opts.items() if k != 'has_header'}
df = pl.read_excel(src, engine='xlsx2csv', read_options=opts)
else:
raise Prevention
- Never duplicate has_header between the top-level parameter and read_options
- Coerce config-sourced booleans with bool(...) — YAML/JSON give ints, numpy gives np.bool_, and the engine's `is not` check rejects both
- When refactoring csv code, strip read_csv kwargs that polars already exposes as parameters
When it happens
Trigger: pl.read_excel(src, engine='xlsx2csv', read_options={'has_header': False}) with has_header left at its default True; or read_options={'has_header': 0}/np.bool_(False)/1 with a matching top-level value — `0 is not False` evaluates True, so it raises despite being semantically equal.
Common situations: Config-driven pipelines (YAML/JSON produce ints for booleans), pandas/numpy flag values passed through, and code that duplicates the header setting between read_options and the top-level parameter.
Related errors
- cannot specify both `columns` and `read_options["columns"]`
- cannot specify both `infer_schema_length` and `read_options[
- cannot specify columns in both `schema_overrides` and `read_
- the `table_name` parameter is not supported by the 'xlsx2csv
- cannot specify both `sheet_name` ({sheet_name!r}) and `sheet
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/f1b5ffe8290fec7b.
Report an issue: GitHub.