pandas-dev/pandas · error · ValueError

The categories must be provided in 'categories' or 'dtype'.

Error message

The categories must be provided in 'categories' or 'dtype'. Both were None.

What it means

Raised by Categorical.from_codes when neither `categories` nor a `dtype` (CategoricalDtype) carrying categories is supplied. from_codes constructs a categorical directly from integer codes, so it cannot infer categories from data; the categories must be provided explicitly to give the codes meaning.

Source

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

        codes : The category codes of the categorical.
        CategoricalIndex : An Index with an underlying ``Categorical``.

        Examples
        --------
        >>> dtype = pd.CategoricalDtype(["a", "b"], ordered=True)
        >>> pd.Categorical.from_codes(codes=[0, 1, 0, 1], dtype=dtype)
        ['a', 'b', 'a', 'b']
        Categories (2, str): ['a' < 'b']
        """
        dtype = CategoricalDtype._from_values_or_dtype(
            categories=categories, ordered=ordered, dtype=dtype
        )
        if dtype.categories is None:
            msg = (
                "The categories must be provided in 'categories' or "
                "'dtype'. Both were None."
            )
            raise ValueError(msg)

        if validate:
            # beware: non-valid codes may segfault
            codes = cls._validate_codes_for_dtype(codes, dtype=dtype)

        return cls._simple_new(codes, dtype=dtype)

    # ------------------------------------------------------------------
    # Categories/Codes/Ordered

    @property
    def categories(self) -> Index:
        """
        The categories of this categorical.

        Setting assigns new values to each category (effectively a rename of
        each individual category).

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Pass categories: `pd.Categorical.from_codes([0,1,0], categories=['a','b'])`.
  2. Pass a CategoricalDtype: `pd.Categorical.from_codes([0,1,0], dtype=pd.CategoricalDtype(['a','b']))`.
  3. If categories are unknown, use the normal constructor `pd.Categorical(values)` instead of from_codes.

Example fix

# before
pd.Categorical.from_codes([0, 1, 0])
# after
pd.Categorical.from_codes([0, 1, 0], categories=['a', 'b'])
Defensive patterns

Strategy: validation

Validate before calling

def from_codes_safe(codes, categories=None, dtype=None):
    import pandas as pd
    if categories is None and (dtype is None or getattr(dtype, 'categories', None) is None):
        raise ValueError("supply categories= or a CategoricalDtype")
    return pd.Categorical.from_codes(codes, categories=categories, dtype=dtype)

Type guard

def has_categories(categories, dtype) -> bool:
    import pandas as pd
    return categories is not None or (isinstance(dtype, pd.CategoricalDtype) and dtype.categories is not None)

Try / catch

try:
    cat = pd.Categorical.from_codes(codes)
except ValueError as e:
    if "categories" in str(e):
        cat = pd.Categorical.from_codes(codes, categories=inferred_categories)
    else:
        raise

Prevention

When it happens

Trigger: `pd.Categorical.from_codes([0,1,0])` with no categories= and no dtype=. The _from_values_or_dtype helper returns a dtype with categories=None and from_codes refuses to proceed.

Common situations: Refactoring code that used the regular constructor; copying only the codes array during serialization without persisting the category list.

Related errors


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