pandas-dev/pandas · error · IndexError

index is out of bounds: must be an integer between

Error message

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

What it means

IndexError from SparseArray._get_val_at when a positional location resolves outside [-n, n-1]. Locations are normalized for negatives, then checked against the array length before sparse-index lookup.

Solutions

  1. Bounds-check before access: if -n <= i < n: ... else handle.
  2. Use pd.Series(arr, index=labels)[label] for label-based access.
  3. Use arr.take([i], allow_fill=True) to get fill_value instead of an IndexError for -1 sentinel.

Example fix

// before
val = arr[5]  # raises if len(arr) < 6
// after
val = arr[5] if 0 <= 5 < len(arr) else fill_default
Defensive patterns

Strategy: validation

Validate before calling

def in_bounds(arr, i) -> bool:
    n = len(arr)
    return -n <= i < n

Type guard

def is_in_range(arr, i) -> bool:
    n = len(arr)
    return -n <= i < n

Try / catch

try:
    arr[i]
except IndexError as e:
    if "out of bounds" in str(e):
        out = fill_default
    else:
        raise

Prevention

When it happens

Trigger: arr[5] on a length-3 SparseArray; arr[-10] on a length-3 array; an integer key that is a valid Python int but out of range.

Common situations: Off-by-one loops over arr length; passing a DataFrame label that is also an integer and being mistaken for a position.

Related errors


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

Appendix: source

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

            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 3b7651241d)