matplotlib/matplotlib · error · AttributeError

'Spines' object does not contain a '{name}' spine

Error message

'Spines' object does not contain a '{name}' spine

What it means

Spines maps attribute access onto its internal dict: ax.spines.left returns the 'left' Spine. When the name is neither a real Spines attribute nor a key in the dict, __getattr__ raises this AttributeError. Typical causes: a typo ('leftt'), a name that does not exist on that Axes class ('polar' on a Cartesian Axes), or access after the spine was removed with del ax.spines['top'].

Source

Thrown at lib/matplotlib/spines.py:590

    """
    def __init__(self, **kwargs):
        self._dict = kwargs

    @classmethod
    def from_dict(cls, d):
        return cls(**d)

    def __getstate__(self):
        return self._dict

    def __setstate__(self, state):
        self.__init__(**state)

    def __getattr__(self, name):
        try:
            return self._dict[name]
        except KeyError:
            raise AttributeError(
                f"'Spines' object does not contain a '{name}' spine")

    def __getitem__(self, key):
        if isinstance(key, list):
            unknown_keys = [k for k in key if k not in self._dict]
            if unknown_keys:
                raise KeyError(', '.join(unknown_keys))
            return SpinesProxy({k: v for k, v in self._dict.items()
                                if k in key})
        if isinstance(key, tuple):
            raise ValueError('Multiple spines must be passed as a single list')
        if isinstance(key, slice):
            if key.start is None and key.stop is None and key.step is None:
                return SpinesProxy(self._dict)
            else:
                raise ValueError(
                    'Spines does not support slicing except for the fully '
                    'open slice [:] to access all spines.')

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Check membership first: if 'top' in ax.spines: ax.spines['top'].set_visible(False)
  2. Use the documented names: 'left', 'right', 'top', 'bottom' plus projection-specific ones ('polar', 'inner', 'geo')
  3. Re-add a deleted spine with ax.spines['top'] = Spine.linear_spine(ax, 'top')

Example fix

# before
ax.spines.toop.set_visible(False)  # AttributeError: no 'toop' spine

# after
if 'top' in ax.spines:
    ax.spines['top'].set_visible(False)
Defensive patterns

Strategy: type-guard

Validate before calling

if 'top' in ax.spines:          # membership check before access
    ax.spines['top'].set_visible(False)

Type guard

def has_spine(ax, name: str) -> bool:
    return name in ax.spines  # hasattr(ax.spines, name) also works

Try / catch

try:
    spine = ax.spines[name]
except (AttributeError, KeyError):
    spine = None  # spine absent on this Axes type; skip styling

Prevention

When it happens

Trigger: ax.spines.leftt; getattr(ax.spines, name) for configured names that are absent on this Axes; ax.spines.top after `del ax.spines['top']`.

Common situations: Style code written for one Axes type reused on another (polar, 3D, geo); config-driven spine styling where names come from a file and are never checked.

Related errors


AI-assisted analysis of matplotlib/matplotlib@b379c1b69e (2026-08-21). Data as JSON: /api/errors/15d670dee5a99c7c. Report an issue: GitHub.