pandas-dev/pandas · error · ValueError

invalid na_position: {na_position!r}

Error message

invalid na_position: {na_position!r}

What it means

Raised by Categorical.sort_values when na_position is not exactly 'last' or 'first'. The numpy-backed sort path (nargsort) only understands these two placements of missing values, so any other string is rejected before sorting begins. This is a strict input-contract violation surfaced as a ValueError.

Source

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

        >>> c
        [NaN, 2, 2, NaN, 5]
        Categories (2, int64): [2, 5]
        >>> c.sort_values()
        [2, 2, 5, NaN, NaN]
        Categories (2, int64): [2, 5]
        >>> c.sort_values(ascending=False)
        [5, 2, 2, NaN, NaN]
        Categories (2, int64): [2, 5]
        >>> c.sort_values(na_position="first")
        [NaN, NaN, 2, 2, 5]
        Categories (2, int64): [2, 5]
        >>> c.sort_values(ascending=False, na_position="first")
        [NaN, NaN, 5, 2, 2]
        Categories (2, int64): [2, 5]
        """
        inplace = validate_bool_kwarg(inplace, "inplace")
        if na_position not in ["last", "first"]:
            raise ValueError(f"invalid na_position: {na_position!r}")

        sorted_idx = nargsort(self, ascending=ascending, na_position=na_position)

        if not inplace:
            codes = self._codes[sorted_idx]
            return self._from_backing_data(codes)
        self._codes[:] = self._codes[sorted_idx]
        return None

    def _rank(
        self,
        *,
        axis: AxisInt = 0,
        method: RankMethod = "average",
        na_option: RankNaOption = "keep",
        ascending: bool = True,
        pct: bool = False,
    ):

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Pass exactly 'last' (default) or 'first' to na_position.
  2. Normalize external input with .lower() and validate against {'first','last'} before forwarding to sort_values.

Example fix

// before
cat.sort_values(na_position='Last')  # ValueError

// after
cat.sort_values(na_position='last')
Defensive patterns

Strategy: validation

Validate before calling

def normalize_na_position(value):
    if value not in ('first', 'last'):
        raise ValueError("na_position must be 'first' or 'last'")
    return value

Type guard

from typing import Literal

NaPosition = Literal['first', 'last']

def is_na_position(v: str) -> bool:
    return v in ('first', 'last')

Try / catch

try:
    cat.sort_values(na_position=pos)
except ValueError as e:
    if 'invalid na_position' in str(e):
        cat.sort_values(na_position='last')
    else:
        raise

Prevention

When it happens

Trigger: Calling .sort_values(na_position=...) with a typo like 'Last', 'FIRST', 'top', 'bottom', None, or an empty string. Also triggered by dynamically passing a user-supplied value without normalization.

Common situations: User-facing code that forwards a config parameter straight to sort_values; locale-dependent casing; or confusion with DataFrame.sort_values which also only accepts 'first'/'last'.

Related errors


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