pandas-dev/pandas · error · ValueError

new categories need to have the same number of items as the

Error message

new categories need to have the same number of items as the old categories!

What it means

Raised by the internal _set_categories (invoked by the `.categories` property setter) when the new categories list has a different length than the current categories. Renaming via the setter requires a 1:1 replacement; adding/removing categories changes the code mapping and must use the dedicated methods.

Source

Thrown at pandas/core/arrays/categorical.py:987

        >>> c
        ['a', 'b']
        Categories (2, str): ['a', 'b']

        >>> c._set_categories(pd.Index(["a", "c"]))
        >>> c
        ['a', 'c']
        Categories (2, str): ['a', 'c']
        """
        if fastpath:
            new_dtype = CategoricalDtype._from_fastpath(categories, self.ordered)
        else:
            new_dtype = CategoricalDtype(categories, ordered=self.ordered)
        if (
            not fastpath
            and self.dtype.categories is not None
            and len(new_dtype.categories) != len(self.dtype.categories)
        ):
            raise ValueError(
                "new categories need to have the same number of "
                "items as the old categories!"
            )

        super().__init__(self._ndarray, new_dtype)

    def _set_dtype(self, dtype: CategoricalDtype, *, copy: bool) -> Self:
        """
        Internal method for directly updating the CategoricalDtype

        Parameters
        ----------
        dtype : CategoricalDtype

        Notes
        -----
        We don't do any validation here. It's assumed that the dtype is
        a (valid) instance of `CategoricalDtype`.

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. To rename in place, supply exactly len(cat.categories) new labels: `cat.categories = ['x','y']` (match count).
  2. To add categories use `cat = cat.add_categories([...])`.
  3. To remove use `cat = cat.remove_categories([...])` or `remove_unused_categories()`.
  4. To fully replace use `cat = cat.set_categories([...])`.

Example fix

# before
cat = pd.Categorical(['a','b'], categories=['a','b'])
cat.categories = ['x', 'y', 'z']
# after
cat = pd.Categorical(['a','b'], categories=['a','b'])
cat.categories = ['x', 'y']
Defensive patterns

Strategy: validation

Validate before calling

def safe_set_categories(cat, new_categories):
    if len(new_categories) != len(cat.categories):
        raise ValueError(f"expected {len(cat.categories)} categories, got {len(new_categories)}")
    cat.categories = new_categories
    return cat

Type guard

def same_category_count(cat, new_categories) -> bool:
    return len(new_categories) == len(cat.categories)

Try / catch

try:
    cat.categories = new_cats
except ValueError as e:
    if 'same number of items' in str(e):
        cat = cat.set_categories(new_cats)
    else:
        raise

Prevention

When it happens

Trigger: `cat.categories = ['x','y','z']` when the current categories have 2 (or 4) entries. The setter delegates to _set_categories which enforces equal length.

Common situations: Renaming categories but accidentally dropping or adding one; chaining operations that change category count then assigning back via `.categories`.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/c0c43ffd8ef8cb39. Report an issue: GitHub.