{"record":{"id":"1bfefd181c0da4c1","repo":"matplotlib/matplotlib","slug":"automatic-legend-placement-loc-best-not-implem","errorCode":null,"errorMessage":"Automatic legend placement (loc='best') not implemented for figure legend","messagePattern":"Automatic legend placement \\(loc='best'\\) not implemented for figure legend","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/matplotlib/legend.py","lineNumber":722,"sourceCode":"            loc = tuple(loc)\n            # validate the tuple represents Real coordinates\n            if len(loc) != 2 or not all(isinstance(e, numbers.Real) for e in loc):\n                raise ValueError(type_err_message)\n        elif isinstance(loc, int):\n            # validate the integer represents a string numeric value\n            if loc < 0 or loc > 10:\n                raise ValueError(type_err_message)\n        else:\n            # all other cases are invalid values of loc\n            raise ValueError(type_err_message)\n\n        if self.isaxes and self._outside_loc:\n            raise ValueError(\n                f\"'outside' option for loc='{loc0}' keyword argument only \"\n                \"works for figure legends\")\n\n        if not self.isaxes and loc == 0:\n            raise ValueError(\n                \"Automatic legend placement (loc='best') not implemented for \"\n                \"figure legend\")\n\n        tmp = self._loc_used_default\n        self._set_loc(loc)\n        self._loc_used_default = tmp  # ignore changes done by _set_loc\n\n    def _set_loc(self, loc):\n        # find_offset function will be provided to _legend_box and\n        # _legend_box will draw itself at the location of the return\n        # value of the find_offset.\n        self._loc_used_default = False\n        self._loc_real = loc\n        self.stale = True\n        self._legend_box.set_offset(self._findoffset)\n\n    def set_ncols(self, ncols):\n        \"\"\"Set the number of columns.\"\"\"","sourceCodeStart":704,"sourceCodeEnd":740,"githubUrl":"https://github.com/matplotlib/matplotlib/blob/b379c1b69e012b142c0f496a52bcb30513802d72/lib/matplotlib/legend.py#L704-L740","documentation":"loc='best' (or code 0) computes the position of least overlap with the plotted artists, which requires a single parent Axes. Figure legends aggregate multiple Axes, so _set_loc raises ValueError('Automatic legend placement ... not implemented for figure legend') when a non-Axes legend gets loc 0.","triggerScenarios":"fig.legend(loc='best'); fig.legend(handles, labels, loc=0); a helper that defaults loc='best' for both ax.legend and fig.legend.","commonSituations":"Refactoring ax.legend calls into fig.legend for multi-axes (twin axes, subplots) layouts; passing loc=0 from a shared constants file; mpl versions where behavior/messages around figure-legend loc defaults changed.","solutions":["Pick an explicit location: fig.legend(loc='upper right') (or codes 1-10).","In shared helpers, default loc='best' only for Axes legends and e.g. 'upper center' for figure legends.","If you truly want minimal overlap, place the legend on a specific axes: axs[0].legend(loc='best')."],"exampleFix":"# before\nfig.legend(loc='best')\n\n# after\nfig.legend(loc='upper center')","handlingStrategy":"fallback","validationCode":"def figure_legend_loc(loc):\n    # 'best'/0 is unsupported on figure legends\n    return 'upper center' if loc in (None, 'best', 0) else loc\n\nfig.legend(handles=hs, loc=figure_legend_loc(requested_loc))","typeGuard":"def needs_axes_for_loc(loc) -> bool:\n    return loc == 'best' or loc == 0","tryCatchPattern":"try:\n    leg = fig.legend(loc='best')\nexcept ValueError:\n    leg = fig.legend(loc='upper right')","preventionTips":["Default figure legends to an explicit loc ('upper center' is typical for multi-axes figures).","Only allow 'best' through code paths that call ax.legend()."],"tags":["matplotlib","legend","figure-legend","loc","not-implemented"],"backgroundTag":"unsupported-operation","analyzedSha":"b379c1b69e012b142c0f496a52bcb30513802d72","analyzedAt":"2026-08-21T23:31:55.468Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}