matplotlib/matplotlib · error · ValueError

The first element of 'code' must be equal to 'MOVETO' ({self

Error message

The first element of 'code' must be equal to 'MOVETO' ({self.MOVETO}).  Your first code is {codes[0]}

What it means

After the shape checks, Path.__init__ additionally requires that a non-empty codes array starts with Path.MOVETO (value 1): every Bezier path must begin with a pen-up move before any line or curve segments. A first code of LINETO, CURVE3, CURVE4, or CLOSEPOLY is undefined behavior in the Agg/renderer path machinery, so it is rejected at construction with a message showing your first code.

Source

Thrown at lib/matplotlib/path.py:139

            line segments of a closed polygon.  Note that the last vertex will
            then be ignored (as the corresponding code will be set to
            `CLOSEPOLY`).
        readonly : bool, optional
            Makes the path behave in an immutable way and sets the vertices
            and codes as read-only arrays.
        """
        vertices = _to_unmasked_float_array(vertices)
        _api.check_shape((None, 2), vertices=vertices)

        if codes is not None and len(vertices):
            codes = np.asarray(codes, self.code_type)
            if codes.ndim != 1 or len(codes) != len(vertices):
                raise ValueError("'codes' must be a 1D list or array with the "
                                 "same length of 'vertices'. "
                                 f"Your vertices have shape {vertices.shape} "
                                 f"but your codes have shape {codes.shape}")
            if len(codes) and codes[0] != self.MOVETO:
                raise ValueError("The first element of 'code' must be equal "
                                 f"to 'MOVETO' ({self.MOVETO}).  "
                                 f"Your first code is {codes[0]}")
        elif closed and len(vertices):
            codes = np.empty(len(vertices), dtype=self.code_type)
            codes[0] = self.MOVETO
            codes[1:-1] = self.LINETO
            codes[-1] = self.CLOSEPOLY

        self._vertices = vertices
        self._codes = codes
        self._interpolation_steps = _interpolation_steps
        self._update_values()

        if readonly:
            self._vertices.flags.writeable = False
            if self._codes is not None:
                self._codes.flags.writeable = False
            self._readonly = True

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Prepend Path.MOVETO: codes = np.r_[Path.MOVETO, codes]
  2. Or build codes wholesale: [Path.MOVETO] + [Path.LINETO] * (n - 2) + [Path.CLOSEPOLY] for a closed polygon
  3. Or omit codes entirely and use Path(verts, closed=True)
  4. When concatenating subpaths, keep each subpath's leading MOVETO and use Path.make_compound_path / compound path utilities

Example fix

// before
p = Path(verts, [Path.LINETO] * len(verts))
// after
p = Path(verts, [Path.MOVETO] + [Path.LINETO] * (len(verts) - 1))
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
from matplotlib.path import Path

def ensure_moveto_first(codes):
    codes = np.asarray(codes).ravel()
    if len(codes) and codes[0] != Path.MOVETO:
        codes = np.r_[Path.MOVETO, codes]
    return codes

Prevention

When it happens

Trigger: Path(verts, [2, 2, 2]) (codes built from LINETO only); slicing codes[1:] and reusing them; concatenating subpath codes where the leading MOVETO was dropped; mapping all codes through a lookup that loses the initial MOVETO.

Common situations: Hand-rolled codes arrays that only track LINETO; path assembly from segment pieces where each piece is expected to be self-contained; converting SVG-ish segment lists and forgetting the initial move command.

Related errors


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