pola-rs/polars · error · TypeError

invalid key type ; expected str or int

Error message

invalid key type {type(key)}; expected str or int

What it means

Categories.__getitem__ only accepts str (name lookup) or int (index lookup). Any other key type (float, slice, list, None) is rejected with a TypeError stating the invalid key type and the expected types.

Solutions

  1. Cast keys explicitly: categories[int(k)] or categories[str(k)]
  2. Iterate and index one key at a time for multiple lookups
  3. Check isinstance(key, (str, int)) before indexing

Example fix

// before
cat = categories[np.int64(2)]
// after
cat = categories[int(np.int64(2))]
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(key, (str, int)) or isinstance(key, bool):
    raise TypeError('key must be str or int')

Type guard

def is_valid_key(key) -> bool:
    return isinstance(key, (str, int)) and not isinstance(key, bool)

Try / catch

try:
    val = categories[key]
except TypeError:
    val = categories[int(key)]  # e.g. numpy scalar

Prevention

When it happens

Trigger: categories[1.0] (numpy int or float instead of Python int); categories[0:2] slicing; passing a list of keys expecting vectorized lookup.

Common situations: Indexing with numpy int64 from array computations without casting; assuming pandas-like list-based indexing is supported.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at py-polars/src/polars/datatypes/classes.py:855

            raise RuntimeError(msg)

    def is_global(self) -> bool:
        """Returns whether this refers to the global categories."""
        return self._categories.is_global()

    def __getitem__(self, key: str | int) -> str | int:
        if isinstance(key, str):
            if (cat := self._categories.get_cat(key)) is None:
                raise KeyError(key)
            return cat
        elif isinstance(key, int):
            if (s := self._categories.cat_to_str(key)) is None:
                msg = f"category index out of range: {key}"
                raise IndexError(msg)
            return s
        else:
            msg = f"invalid key type {type(key)}; expected str or int"
            raise TypeError(msg)

    def __contains__(self, item: str | int) -> bool:
        if isinstance(item, str):
            return self._categories.get_cat(item) is not None
        elif isinstance(item, int):
            return self._categories.cat_to_str(item) is not None
        else:
            return False

    def __iter__(self) -> Iterator[str | None]:
        for i in range(self._categories.num_cats_upper_bound()):
            yield self._categories.cat_to_str(i)

    def to_series(self) -> Series:
        """
        Return a :class:`Series` containing all categories in this `Categories`.

        The categories are ordered by their physical category value.

View on GitHub (pinned to fe841f959e)