matplotlib/matplotlib · error · ValueError

{coords!r} is not a valid coordinate

Error message

{coords!r} is not a valid coordinate

What it means

In Annotation._get_xy_transform: string xycoords values other than the bare keywords 'data' and 'polar' must be exactly two whitespace-separated words - a reference-box name plus a unit, like 'axes fraction'. coords.split() must yield two parts; if it yields one (or three), this ValueError is raised.

Source

Thrown at lib/matplotlib/text.py:1783

        elif isinstance(coords, BboxBase):
            return BboxTransformTo(coords)
        elif isinstance(coords, Transform):
            return coords
        elif not isinstance(coords, str):
            raise TypeError(
                f"'xycoords' must be an instance of str, tuple[str, str], Artist, "
                f"Transform, or Callable, not a {type(coords).__name__}")

        if coords == 'data':
            return self.axes.transData
        elif coords == 'polar':
            from matplotlib.projections import PolarAxes
            return PolarAxes.PolarTransform() + self.axes.transData

        try:
            bbox_name, unit = coords.split()
        except ValueError:  # i.e. len(coords.split()) != 2.
            raise ValueError(f"{coords!r} is not a valid coordinate") from None

        bbox0, xy0 = None, None

        # if unit is offset-like
        if bbox_name == "figure":
            bbox0 = self.get_figure(root=False).figbbox
        elif bbox_name == "subfigure":
            bbox0 = self.get_figure(root=False).bbox
        elif bbox_name == "axes":
            bbox0 = self.axes.bbox

        # reference x, y in display coordinate
        if bbox0 is not None:
            xy0 = bbox0.p0
        elif bbox_name == "offset":
            xy0 = self._get_position_xy(renderer)
        else:
            raise ValueError(f"{coords!r} is not a valid coordinate")

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Use complete two-word specs: 'figure points', 'figure pixels', 'figure fraction', 'subfigure ...', 'axes points', 'axes pixels', 'axes fraction', or 'offset <unit>'.
  2. Check typos and concatenations - the string must contain whitespace between the name and the unit.
  3. For per-axis tuples, make each element a valid spec by itself: ('axes fraction', 'data').

Example fix

# before
ax.annotate('a', xy=(0.5, 0.5), xycoords='axes')  # ValueError

# after
ax.annotate('a', xy=(0.5, 0.5), xycoords='axes fraction')
Defensive patterns

Strategy: validation

Validate before calling

BOXES = {'figure', 'subfigure', 'axes', 'offset'}
UNITS = {'points', 'pixels', 'fraction', 'fontsize'}

def valid_coord_string(c):
    if c in ('data', 'polar'):
        return True
    parts = c.split()
    return len(parts) == 2 and parts[0] in BOXES and parts[1] in UNITS

Prevention

When it happens

Trigger: xycoords='axes' (missing unit); xycoords='figure fraction extra'; typos like 'axes fraction' are fine (split() handles runs) but 'axesfraction' is one word and fails; a bare unknown keyword like 'screen' also fails here since only 'data' and 'polar' are special-cased.

Common situations: Config-driven annotation systems where users type half the spec; porting code that used a single-word spec from another library; forgetting that per-axis tuples must have two-word strings in each slot, e.g. ('axes', 'data') fails on 'axes'.

Related errors


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