{"record":{"id":"42f6f98cae900a87","repo":"matplotlib/matplotlib","slug":"multiple-spines-must-be-passed-as-a-single-list","errorCode":null,"errorMessage":"Multiple spines must be passed as a single list","messagePattern":"Multiple spines must be passed as a single list","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/matplotlib/spines.py","lineNumber":601,"sourceCode":"    def __setstate__(self, state):\n        self.__init__(**state)\n\n    def __getattr__(self, name):\n        try:\n            return self._dict[name]\n        except KeyError:\n            raise AttributeError(\n                f\"'Spines' object does not contain a '{name}' spine\")\n\n    def __getitem__(self, key):\n        if isinstance(key, list):\n            unknown_keys = [k for k in key if k not in self._dict]\n            if unknown_keys:\n                raise KeyError(', '.join(unknown_keys))\n            return SpinesProxy({k: v for k, v in self._dict.items()\n                                if k in key})\n        if isinstance(key, tuple):\n            raise ValueError('Multiple spines must be passed as a single list')\n        if isinstance(key, slice):\n            if key.start is None and key.stop is None and key.step is None:\n                return SpinesProxy(self._dict)\n            else:\n                raise ValueError(\n                    'Spines does not support slicing except for the fully '\n                    'open slice [:] to access all spines.')\n        return self._dict[key]\n\n    def __setitem__(self, key, value):\n        # TODO: Do we want to deprecate adding spines?\n        self._dict[key] = value\n\n    def __delitem__(self, key):\n        # TODO: Do we want to deprecate deleting spines?\n        del self._dict[key]\n\n    def __iter__(self):","sourceCodeStart":583,"sourceCodeEnd":619,"githubUrl":"https://github.com/matplotlib/matplotlib/blob/b379c1b69e012b142c0f496a52bcb30513802d72/lib/matplotlib/spines.py#L583-L619","documentation":"Spines.__getitem__ accepts a single name (string), a list of names, or the fully open slice [:]. Indexing with a tuple - classically ax.spines[('top', 'right')] written where ax.spines[['top', 'right']] was meant - raises this ValueError; the message states the fix: multiple names must be passed as a single list.","triggerScenarios":"ax.spines[('top', 'right')].set_visible(False) - parentheses around the names instead of a list literal; programmatic key building that produces a tuple.","commonSituations":"Habits carried over from pandas .loc tuple indexing or numpy multi-axis indexing; list-vs-tuple normalization missing in helper functions.","solutions":["Use a list: ax.spines[['top', 'right']].set_visible(False)","Address all spines with ax.spines[:]","Normalize keys before indexing: names = list(names) if isinstance(names, tuple) else names"],"exampleFix":"# before\nax.spines[('top', 'right')].set_visible(False)  # ValueError\n\n# after\nax.spines[['top', 'right']].set_visible(False)","handlingStrategy":"validation","validationCode":"def spine_keys(names):\n    if isinstance(names, tuple):\n        names = list(names)  # tuple key would raise inside __getitem__\n    return names\n\nax.spines[spine_keys(('top', 'right'))].set_visible(False)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write multiple spine names as a list literal: ax.spines[['top', 'right']]","Do not carry tuple-indexing habits from pandas .loc into Spines","Normalize programmatic keys to lists before indexing"],"tags":["matplotlib","spines","indexing","valueerror"],"backgroundTag":"invalid-spines-index","analyzedSha":"b379c1b69e012b142c0f496a52bcb30513802d72","analyzedAt":"2026-08-21T23:31:55.468Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}