pandas-dev/pandas · error · ValueError
invalid na_position: {na_position}
Error message
invalid na_position: {na_position} What it means
Raised by ArrowExtensionArray.argsort when na_position is not 'last' or 'first'. The mapping dict at line 1519 only accepts those two strings; any other value (typo, 'Last', 'end', None, 'at_end') yields null_placement None and raises ValueError. Used by argsort/sort/sort_values pipelines.
Source
Thrown at pandas/core/arrays/arrow/array.py:1521
>>> pd.array([True, False, pd.NA], dtype="boolean[pyarrow]").all(skipna=False)
False
>>> pd.array([1, 0, pd.NA], dtype="boolean[pyarrow]").all(skipna=False)
False
"""
return self._reduce("all", skipna=skipna, **kwargs)
def argsort(
self,
*,
ascending: bool = True,
kind: SortKind = "quicksort",
na_position: str = "last",
**kwargs,
) -> np.ndarray:
order = "ascending" if ascending else "descending"
null_placement = {"last": "at_end", "first": "at_start"}.get(na_position, None)
if null_placement is None:
raise ValueError(f"invalid na_position: {na_position}")
result = pc.array_sort_indices(
self._pa_array, order=order, null_placement=null_placement
)
np_result = result.to_numpy()
return np_result.astype(np.intp, copy=False)
def sort(
self,
*,
ascending: bool = True,
kind: SortKind = "quicksort",
na_position: str = "last",
) -> None:
# This override replaces self._pa_array directly, bypassing __setitem__,
# so enforce the read-only guard here to stay consistent with it and
# with the base ExtensionArray.sort.
if self._readonly:View on GitHub (pinned to 71959b8cb9)
Solutions
- Use only 'first' or 'last' (lowercase).
- Normalize user input: na_position = str(na_position).lower(); assert in {'first','last'}.
- Map synonyms: {'at_start':'first','at_end':'last','head':'first','tail':'last'}.
- Default to 'last' when input is missing/invalid.
Example fix
# before
idx = arr.argsort(na_position=cfg.get('na', 'at_end')) # ValueError
# after
NA_MAP = {'at_start':'first','at_end':'last','first':'first','last':'last'}
na = NA_MAP[str(cfg.get('na','last')).lower()]
idx = arr.argsort(na_position=na) Defensive patterns
Strategy: validation
Validate before calling
VALID_NA_POSITIONS = {'first', 'last'}
SYNONYMS = {'at_start': 'first', 'at_end': 'last', 'head': 'first', 'tail': 'last', 'top': 'first', 'bottom': 'last'}
def normalize_na_position(x):
if x is None:
return 'last'
key = str(x).lower()
return SYNONYMS.get(key, key) if key in SYNONYMS else (key if key in VALID_NA_POSITIONS else 'last')
na = normalize_na_position(cfg.get('na_position'))
idx = arr.argsort(na_position=na) Type guard
def is_valid_na_position(x) -> bool:
return isinstance(x, str) and x.lower() in {'first', 'last'} Try / catch
try:
idx = arr.argsort(na_position=na)
except ValueError as e:
if 'invalid na_position' in str(e):
idx = arr.argsort(na_position='last')
else:
raise Prevention
- Normalize na_position from external config to 'first'/'last' (lowercase).
- Validate the value against {'first','last'} before passing.
- Map pyarrow-internal synonyms (at_start/at_end) to the public vocabulary.
When it happens
Trigger: `arr.argsort(na_position='end')`, `arr.argsort(na_position=None)`, `arr.argsort(na_position='LAST')`, or passing a user-controlled config value without validation. Case-sensitive: only lowercase 'first'/'last'.
Common situations: Config-driven sort parameters from YAML/JSON where values differ ('at_end', 'tail'); case mismatches; passing pandas-internal null_placement strings ('at_start'/'at_end') that pyarrow uses but argsort doesn't accept at the public API.
Related errors
- name_or_index must be an int, str, bytes, pyarrow.compute.Ex
- Cannot modify read-only array
- Only np.ndarray, ExtensionArray, and Index objects are allow
- Invalid side: {side}. Side must be one of 'left', 'right', '
- invalid normalization form
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/5c39aee9369356b5.
Report an issue: GitHub.