pandas-dev/pandas · error · IndexError

index is out of bounds: must be an integer between -{n} and

Error message

index is out of bounds: must be an integer between -{n} and {n - 1}

What it means

Raised by SparseArray._get_val_at when the requested integer position loc is outside [-n, n-1] after negative-index normalization. It mirrors numpy IndexError semantics but is emitted from the sparse lookup path so the caller learns the valid bounds before the sp_index is consulted. Negative indices are folded by adding len(self) once; anything still negative or >= n is rejected.

Source

Thrown at pandas/core/arrays/sparse/array.py:1135

            if com.is_bool_indexer(key):
                # mypy doesn't know we have an array here
                key = cast("np.ndarray", key)
                return self.take(np.arange(len(key), dtype=np.int32)[key])
            elif hasattr(key, "__len__"):
                return self.take(key)
            else:
                raise ValueError(f"Cannot slice with '{key}'")

        return type(self)(data_slice, kind=self.kind)

    def _get_val_at(self, loc):
        n = len(self)
        if loc < 0:
            loc += n

        if loc >= n or loc < 0:
            raise IndexError(
                f"index is out of bounds: must be an integer between -{n} and {n - 1}"
            )

        sp_loc = self.sp_index.lookup(loc)
        if sp_loc == -1:
            return self.fill_value
        else:
            val = self.sp_values[sp_loc]
            val = maybe_box_datetimelike(val, self.sp_values.dtype)
            return val

    def take(self, indices, *, allow_fill: bool = False, fill_value=None) -> Self:
        if is_scalar(indices):
            raise ValueError(f"'indices' must be an array, not a scalar '{indices}'.")
        indices = np.asarray(indices, dtype=np.int32)

        dtype = None
        if indices.size == 0:

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Clamp/normalize the index before lookup: loc = loc if loc >= 0 else loc + len(arr); assert 0 <= loc < len(arr).
  2. Use sparse_arr.iloc[loc] semantics via a Series wrapper which standardizes bounds handling.
  3. Recompute the length at call time instead of reusing a cached n.

Example fix

// before
n = len(arr)
val = arr._get_val_at(user_pos)  # user_pos may exceed n

// after
loc = user_pos % len(arr)  # or an explicit bounds check
val = arr._get_val_at(loc)
Defensive patterns

Strategy: validation

Validate before calling

def safe_get_val_at(arr, loc):
    n = len(arr)
    loc = loc + n if loc < 0 else loc
    if not (0 <= loc < n):
        raise IndexError(f'loc {loc} out of range for length {n}')
    return arr._get_val_at(loc)

Type guard

def is_in_bounds(arr, loc) -> bool:
    n = len(arr)
    return -n <= loc < n

Try / catch

try:
    val = arr._get_val_at(loc)
except IndexError as e:
    if 'out of bounds' in str(e):
        # handle missing position
        val = arr.fill_value
    else:
        raise

Prevention

When it happens

Trigger: Calling _get_val_at(loc) with loc >= len(sparse_arr) or loc < -len(sparse_arr), often indirectly via argmax/argmin internals, repr formatting of a stale cached index, or user code computing positions from a different-length array.

Common situations: Off-by-one loops, using .idxmax() results from one Series to index another of different length, caching a length then appending/trimming the array, or negative indexing math that overshoots (e.g. loc=-n-1).

Related errors


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