pola-rs/polars · error · ValueError

cannot specify both `sheet_name` ({sheet_name!r}) and `sheet

Error message

cannot specify both `sheet_name` ({sheet_name!r}) and `sheet_id` ({sheet_id!r})

What it means

Raised by polars.read_excel and pl.read_ods in _get_sheet_names (a ValueError) when both sheet_id and sheet_name are not None. Sheet selection is mutually exclusive by design: id-based and name-based selection cannot be combined, even when they point at the same sheet. This check runs before any worksheet data is parsed.

Source

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

        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."""
    if sheet_id is not None and sheet_name is not None:
        msg = f"cannot specify both `sheet_name` ({sheet_name!r}) and `sheet_id` ({sheet_id!r})"
        raise ValueError(msg)

    sheet_names = []
    if sheet_id is None and sheet_name is None:
        name = None if table_name else worksheets[0]["name"]
        sheet_names.append(name)
        return_multiple_sheets = False
    elif sheet_id == 0:
        sheet_names.extend(ws["name"] for ws in worksheets)
        return_multiple_sheets = True
    else:
        return_multiple_sheets = (
            (isinstance(sheet_name, Sequence) and not isinstance(sheet_name, str))
            or isinstance(sheet_id, Sequence)
            or sheet_id == 0
        )
        if names := (
            (sheet_name,) if isinstance(sheet_name, str) else sheet_name or ()
        ):

View on GitHub (pinned to df599052da)

Solutions

  1. Pass exactly one of sheet_id or sheet_name; explicitly set the other to None
  2. In wrapper functions, resolve the config to a single selector before calling read_excel
  3. Remember sheet_id is 1-based and 0 means 'all sheets' when choosing which side to keep

Example fix

# before
pl.read_excel('f.xlsx', sheet_id=1, sheet_name='Sheet1')

# after
pl.read_excel('f.xlsx', sheet_name='Sheet1')
Defensive patterns

Strategy: validation

Validate before calling

def resolve_sheet_selector(sheet_id, sheet_name):
    if sheet_id is not None and sheet_name is not None:
        raise ValueError('pass either sheet_id or sheet_name, not both')
    return sheet_id, sheet_name

sheet_id, sheet_name = resolve_sheet_selector(sheet_id, sheet_name)
df = pl.read_excel(src, sheet_id=sheet_id, sheet_name=sheet_name)

Try / catch

try:
    df = pl.read_excel(src, sheet_id=sheet_id, sheet_name=sheet_name)
except ValueError as e:
    if 'cannot specify both' in str(e):
        df = pl.read_excel(src, sheet_name=sheet_name)  # keep one selector
    else:
        raise

Prevention

When it happens

Trigger: pl.read_excel('f.xlsx', sheet_id=1, sheet_name='Sheet1'); pl.read_ods('f.ods', sheet_id=[1,2], sheet_name='data'); also fires when wrapper code computes both values from defaults and ends up passing neither as None.

Common situations: Config-driven loaders where sheet_id and sheet_name are both populated 'to be safe'; refactoring code from name-based to position-based selection while forgetting to remove the old argument; passing **kwargs dictionaries that carry both keys.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/fd22504dc489e1fd. Report an issue: GitHub.