pola-rs/polars · error · ParameterCollisionError

cannot specify both `columns` and `read_options["columns"]`

Error message

cannot specify both `columns` and `read_options["columns"]`

What it means

With the xlsx2csv engine, top-level `columns` and `read_options["columns"]` both select output columns; passing both (with a truthy `columns`) is ambiguous, so `_get_read_options` raises `ParameterCollisionError`.

Solutions

  1. Use only the top-level `columns=` argument and remove the key from read_options
  2. Or keep `read_options['columns']` and omit `columns=`
  3. Guard shared read_options dicts to strip engine-specific keys before merging

Example fix

// before
pl.read_excel(f, engine='xlsx2csv', columns=['a'], read_options={'columns': 'a'})
// after
pl.read_excel(f, engine='xlsx2csv', columns=['a'])
Defensive patterns

Strategy: validation

Validate before calling

assert not (columns and 'columns' in read_options), 'columns conflict'

Try / catch

from polars.exceptions import ParameterCollisionError
try:
    pl.read_excel(f, engine='xlsx2csv', columns=cols, read_options=ro)
except ParameterCollisionError:
    ro.pop('columns', None)
    df = pl.read_excel(f, engine='xlsx2csv', columns=cols, read_options=ro)

Prevention

When it happens

Trigger: `pl.read_excel(..., engine='xlsx2csv', columns=['a'], read_options={'columns': 'a,b'})` — any call where both are supplied.

Common situations: Mixing the newer top-level `columns` API with legacy read_options-based column selection; templated configs that always inject a 'columns' key.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/57f6628d3d3cc3d5. Report an issue: GitHub.

Appendix: source

Thrown at py-polars/src/polars/io/spreadsheet/functions.py:773

        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,
    sheet_name: str | Sequence[str] | None,
    table_name: str | None,
    worksheets: list[dict[str, Any]],
) -> tuple[list[str], bool]:
    """Establish sheets to read; indicate if we are returning a dict frames."""

View on GitHub (pinned to fe841f959e)