{"record":{"id":"1523009fbd0aa686","repo":"pandas-dev/pandas","slug":"columns-not-found-str-bad-keys-1-1","errorCode":null,"errorMessage":"Columns not found: {str(bad_keys)[1:-1]}","messagePattern":"Columns not found: (.+?)","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"pandas/core/base.py","lineNumber":219,"sourceCode":"        if isinstance(self.obj, ABCSeries):\n            return self.obj\n\n        if self._selection is not None:\n            return self.obj[self._selection_list]\n\n        if len(self.exclusions) > 0:\n            return self.obj._drop_axis(self.exclusions, axis=1)\n        else:\n            return self.obj\n\n    def __getitem__(self, key):\n        if self._selection is not None:\n            raise IndexError(f\"Column(s) {self._selection} already selected\")\n\n        if isinstance(key, (list, tuple, ABCSeries, ABCIndex, np.ndarray)):\n            if len(self.obj.columns.intersection(key)) != len(set(key)):\n                bad_keys = list(set(key).difference(self.obj.columns))\n                raise KeyError(f\"Columns not found: {str(bad_keys)[1:-1]}\")\n            return self._gotitem(list(key), ndim=2)\n\n        else:\n            if key not in self.obj:\n                raise KeyError(f\"Column not found: {key}\")\n            ndim = self.obj[key].ndim\n            return self._gotitem(key, ndim=ndim)\n\n    def _gotitem(self, key, ndim: int, subset=None):\n        \"\"\"\n        sub-classes to define\n        return a sliced object\n\n        Parameters\n        ----------\n        key : str / list of selections\n        ndim : {1, 2}\n            requested ndim of result","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/base.py#L201-L237","documentation":"Raised by SelectionMixin.__getitem__ when a list/tuple/Series/Index/array key references one or more columns not present in obj.columns. The bad keys are reported by name. Distinguishes list-key lookups from single-key lookups.","triggerScenarios":"`df.groupby('g')[['a','x']]` where 'x' is not a column; selecting missing columns from a groupby/rolling selection mixin holder.","commonSituations":"Typo in column names; columns renamed/dropped upstream; dynamic column lists sourced from config.","solutions":["Validate the list against obj.columns before selection: `[c for c in cols if c in df.columns]`.","Fix upstream renames so expected columns exist.","Use errors-tolerant selection or assert the schema at load time."],"exampleFix":"// before\ng = df.groupby('g')[['a','mistake']]\n\n// after\ncols = ['a','mistake']\npresent = [c for c in cols if c in df.columns]\ng = df.groupby('g')[present]","handlingStrategy":"validation","validationCode":"missing = set(keys) - set(obj.columns)\nif missing:\n    raise KeyError(f'columns not found: {missing}')","typeGuard":"def all_columns_present(df, keys) -> bool:\n    return set(keys).issubset(df.columns)","tryCatchPattern":"try:\n    sub = holder[keys]\nexcept KeyError as e:\n    if 'not found' in str(e):\n        keys = [k for k in keys if k in holder.obj.columns]\n        sub = holder[keys]\n    else:\n        raise","preventionTips":["Validate column lists against df.columns.","Strip/normalize column names at load.","Assert schema upstream."],"tags":["selection","groupby","keyerror","columns"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}