pandas-dev/pandas · error · IndexError

Column(s) already selected

Error message

Column(s) {self._selection} already selected

What it means

Raised by SelectionMixin.__getitem__ when a selection has already been made on the group-like object (its `_selection` is not None) and the caller tries to index into it again. Because the object already represents a fixed column subset, further column selection is ambiguous and is rejected as IndexError, directing the user to restructure the selection.

Solutions

  1. Make the column selection once, at the groupby/rolling construction site, instead of chaining.
  2. Recreate the groupby/rolling object from the original DataFrame if a different column subset is needed.
  3. Access the underlying object via `.obj` and re-select from there.

Example fix

# before
g = df.groupby('a')['b']
g['c']  # IndexError: Column(s) b already selected

# after
g = df.groupby('a')[['b', 'c']]
Defensive patterns

Strategy: validation

Validate before calling

def can_select_again(selection_obj) -> bool:
    # SelectionMixin objects whose _selection is set will reject further indexing
    return getattr(selection_obj, '_selection', None) is None

Type guard

def selection_already_made(selection_obj) -> bool:
    return getattr(selection_obj, '_selection', None) is not None

Try / catch

try:
    sub = group_selection[key]
except IndexError as e:
    if 'already selected' in str(e):
        sub = group_selection.obj[key]  # re-select from the underlying object
    else:
        raise

Prevention

When it happens

Trigger: Calling `g['col']` (or a list/tuple/Series/Index/array key) on a groupby/rolling/resampler selection object that was already created with a column selection, e.g. `df.groupby('a')['b']['c']`.

Common situations: Chaining selection operations on groupby/rolling objects; code refactors that combine a column selection with a later `.getitem` call.

Related errors


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

Appendix: source

Thrown at pandas/core/base.py:214

        return self._selected_obj.ndim

    @final
    @cache_readonly
    def _obj_with_exclusions(self):
        if isinstance(self.obj, ABCSeries):
            return self.obj

        if self._selection is not None:
            return self.obj[self._selection_list]

        if len(self.exclusions) > 0:
            return self.obj._drop_axis(self.exclusions, axis=1)
        else:
            return self.obj

    def __getitem__(self, key):
        if self._selection is not None:
            raise IndexError(f"Column(s) {self._selection} already selected")

        if isinstance(key, (list, tuple, ABCSeries, ABCIndex, np.ndarray)):
            if len(self.obj.columns.intersection(key)) != len(set(key)):
                bad_keys = list(set(key).difference(self.obj.columns))
                raise KeyError(f"Columns not found: {str(bad_keys)[1:-1]}")
            return self._gotitem(list(key), ndim=2)

        else:
            if key not in self.obj:
                raise KeyError(f"Column not found: {key}")
            ndim = self.obj[key].ndim
            return self._gotitem(key, ndim=ndim)

    def _gotitem(self, key, ndim: int, subset=None):
        """
        sub-classes to define
        return a sliced object

View on GitHub (pinned to 3b7651241d)