{"record":{"id":"23401fb530ebd49b","repo":"matplotlib/matplotlib","slug":"unknown-spine-spine-type-self-spine-type-r","errorCode":null,"errorMessage":"unknown spine spine_type: {self.spine_type!r}","messagePattern":"unknown spine spine_type: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/matplotlib/spines.py","lineNumber":386,"sourceCode":"        \"\"\"Return the spine transform.\"\"\"\n        self._ensure_position_is_set()\n\n        position = self._position\n        if isinstance(position, str):\n            if position == 'center':\n                position = ('axes', 0.5)\n            elif position == 'zero':\n                position = ('data', 0)\n        assert len(position) == 2, 'position should be 2-tuple'\n        position_type, amount = position\n        _api.check_in_list(['axes', 'outward', 'data'],\n                           position_type=position_type)\n        if self.spine_type in ['left', 'right']:\n            base_transform = self.axes.get_yaxis_transform(which='grid')\n        elif self.spine_type in ['top', 'bottom']:\n            base_transform = self.axes.get_xaxis_transform(which='grid')\n        else:\n            raise ValueError(f'unknown spine spine_type: {self.spine_type!r}')\n\n        if position_type == 'outward':\n            if amount == 0:  # short circuit commonest case\n                return base_transform\n            else:\n                offset_vec = {'left': (-1, 0), 'right': (1, 0),\n                              'bottom': (0, -1), 'top': (0, 1),\n                              }[self.spine_type]\n                # calculate x and y offset in dots\n                offset_dots = amount * np.array(offset_vec) / 72\n                return (base_transform\n                        + mtransforms.ScaledTranslation(\n                            *offset_dots, self.get_figure(root=False).dpi_scale_trans))\n        elif position_type == 'axes':\n            if self.spine_type in ['left', 'right']:\n                # keep y unchanged, fix x at amount\n                return (mtransforms.Affine2D.from_values(0, 0, 0, 1, amount, 0)\n                        + base_transform)","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/matplotlib/matplotlib/blob/b379c1b69e012b142c0f496a52bcb30513802d72/lib/matplotlib/spines.py#L368-L404","documentation":"Spine.get_spine_transform selects the spine's base transform from spine_type: get_yaxis_transform(which='grid') for 'left'/'right' and get_xaxis_transform(which='grid') for 'top'/'bottom'. No other type has a branch, so a ValueError is raised. set_position() always calls get_spine_transform, and even get_position() triggers it lazily through _ensure_position_is_set, so any position operation on a custom-typed spine raises this error.","triggerScenarios":"spine.set_position(pos) or spine.get_position() on a Spine whose spine_type is a custom name such as 'geo'; generic code that applies the position machinery to every spine including projection spines.","commonSituations":"Cartopy-style GeoSpine and other projection spines carry custom spine_types; matplotlib core deliberately never calls the position machinery on non-Cartesian spines (see _ensure_transform_is_set), but user styling helpers that do hit this error.","solutions":["Guard by spine_type: only call set_position/get_position on 'left', 'right', 'top', 'bottom' spines","Override set_position, get_position, and get_spine_transform together in the Spine subclass (cartopy raises NotImplementedError from set_position)","Register the spine under a standard type if it genuinely should support Cartesian positioning"],"exampleFix":"# before\nspine = Spine(ax, 'geo', path)\nspine.set_position(('outward', 5))  # ValueError: unknown spine spine_type: 'geo'\n\n# after\nclass GeoSpine(Spine):\n    def set_position(self, position):\n        raise NotImplementedError('projection spine cannot be repositioned')\n    def get_spine_transform(self):\n        return self.axes.transAxes\nspine = GeoSpine(ax, 'geo', path)","handlingStrategy":"type-guard","validationCode":"CARTESIAN = ('left', 'right', 'top', 'bottom')\n\n# guard generic styling loops\nfor spine in ax.spines.values():\n    if getattr(spine, 'spine_type', '') in CARTESIAN:\n        spine.set_position(('outward', 5))","typeGuard":"def can_position_spine(spine) -> bool:\n    return getattr(spine, 'spine_type', None) in ('left', 'right', 'top', 'bottom')","tryCatchPattern":"try:\n    spine.set_position(pos)\nexcept ValueError as err:\n    if 'unknown spine spine_type' in str(err):\n        pass  # projection/custom spine: positioning not supported, skip\n    else:\n        raise","preventionTips":["Treat set_position/get_position as Cartesian-spine-only APIs","In Spine subclasses with custom types, override set_position and get_spine_transform together","Style projection spines through their Axes APIs (theta offset, extent), not spine positions"],"tags":["matplotlib","spines","spine-type","transform"],"backgroundTag":"unsupported-spine-type","analyzedSha":"b379c1b69e012b142c0f496a52bcb30513802d72","analyzedAt":"2026-08-21T23:31:55.468Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}