{"record":{"id":"988f35ef11cdbc5f","repo":"pola-rs/polars","slug":"the-values-of-has-header-and-read-options-head","errorCode":null,"errorMessage":"the values of `has_header` and `read_options[\"header_row\"]` are not compatible","messagePattern":"the values of `has_header` and `read_options\\[\"header_row\"\\]` are not compatible","errorType":"exception","errorClass":"ParameterCollisionError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/spreadsheet/functions.py","lineNumber":743,"sourceCode":"\ndef _get_read_options(\n    read_options: dict[str, Any] | None,\n    *,\n    engine: ExcelSpreadsheetEngine,\n    columns: Sequence[int] | Sequence[str] | None,\n    infer_schema_length: int | None,\n    has_header: bool,\n) -> dict[str, Any]:\n    \"\"\"Normalise top-level parameters to engine-specific 'read_options' dict.\"\"\"\n    read_options = (read_options or {}).copy()\n\n    if engine == \"calamine\":\n        if (\"use_columns\" in read_options) and columns:\n            msg = 'cannot specify both `columns` and `read_options[\"use_columns\"]`'\n            raise ParameterCollisionError(msg)\n        elif read_options.get(\"header_row\") is not None and has_header is False:\n            msg = 'the values of `has_header` and `read_options[\"header_row\"]` are not compatible'\n            raise ParameterCollisionError(msg)\n        elif (\"schema_sample_rows\" in read_options) and (\n            infer_schema_length != N_INFER_DEFAULT\n        ):\n            msg = 'cannot specify both `infer_schema_length` and `read_options[\"schema_sample_rows\"]`'\n            raise ParameterCollisionError(msg)\n\n        read_options[\"schema_sample_rows\"] = infer_schema_length\n        if has_header is False and \"header_row\" not in read_options:\n            read_options[\"header_row\"] = None\n\n    elif engine == \"xlsx2csv\":\n        if (\"columns\" in read_options) and columns:\n            msg = 'cannot specify both `columns` and `read_options[\"columns\"]`'\n            raise ParameterCollisionError(msg)\n        elif (\n            \"has_header\" in read_options\n            and read_options[\"has_header\"] is not has_header\n        ):","sourceCodeStart":725,"sourceCodeEnd":761,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/spreadsheet/functions.py#L725-L761","documentation":"polars.exceptions.ParameterCollisionError raised while normalizing read_excel options for the calamine engine: has_header=False contradicts read_options['header_row'] being set. header_row tells calamine which row holds the headers; declaring has_header=False says there is no header at all — the two cannot both hold, so the pair is rejected.","triggerScenarios":"pl.read_excel('f.xlsx', engine='calamine', has_header=False, read_options={'header_row': 0}). Any non-None header_row together with has_header=False.","commonSituations":"Reading headerless exports while reusing a read_options dict built for headered files; porting calamine snippets that always set header_row=0 into polars calls that also set has_header.","solutions":["For headerless data, pass has_header=False and remove 'header_row' from read_options.","To relocate the header, keep read_options={'header_row': N} and leave has_header at its default (True).","Build engine read_options per file shape instead of sharing one dict across headered and headerless inputs."],"exampleFix":"# before\npl.read_excel('f.xlsx', has_header=False, read_options={'header_row': 0})\n\n# after\npl.read_excel('f.xlsx', has_header=False)","handlingStrategy":"validation","validationCode":"ro = dict(read_options or {})\nif has_header is False:\n    ro.pop('header_row', None)  # contradictory with has_header=False\npl.read_excel(path, has_header=has_header, read_options=ro)","typeGuard":null,"tryCatchPattern":"try:\n    pl.read_excel(path, has_header=has_header, read_options=read_options)\nexcept pl.exceptions.ParameterCollisionError as e:\n    if 'header_row' in str(e):\n        ro = {k: v for k, v in (read_options or {}).items() if k != 'header_row'}\n        pl.read_excel(path, has_header=has_header, read_options=ro)\n    else:\n        raise","preventionTips":["has_header=False means no header anywhere — do not also set header_row.","To move the header to row N, keep has_header default and set read_options['header_row']=N.","Build read_options per input file shape; do not share one dict across headered/headerless files."],"tags":["excel","spreadsheet","read-excel","calamine","parameter-conflict","header"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}