roboflow/supervision · error · ValueError

xy must be a 3D np.ndarray with shape {expected_shape}, but

Error message

xy must be a 3D np.ndarray with shape {expected_shape}, but got shape {actual_shape}

What it means

Raised by supervision.validators._validate_xy when constructing KeyPoints: xy must be a 3D np.ndarray whose last dimension is 2 (x, y) or 3 (x, y, confidence), i.e. shape (n_keypoints_objects, m_points_per_object, 2 or 3).

Source

Thrown at src/supervision/validators/__init__.py:221

        else:
            raise ValueError(f"Value for key '{key}' must be a list or np.ndarray")


@deprecated(  # type: ignore[untyped-decorator]
    target=_validate_data,
    deprecated_in="0.29.0",
    remove_in="0.32.0",
)
def validate_data(data: dict[str, Any], n: int) -> None:
    void(data, n)


def _validate_xy(xy: Any, n: int, m: int) -> None:
    expected_shape = f"({n}, {m}, 2) or ({n}, {m}, 3)"
    actual_shape = str(getattr(xy, "shape", None))

    if not isinstance(xy, np.ndarray) or xy.ndim != 3 or xy.shape[2] not in (2, 3):
        raise ValueError(
            f"xy must be a 3D np.ndarray with shape {expected_shape}, but got shape "
            f"{actual_shape}"
        )


@deprecated(  # type: ignore[untyped-decorator]
    target=_validate_xy,
    deprecated_in="0.29.0",
    remove_in="0.32.0",
)
def validate_xy(xy: Any, n: int, m: int) -> None:
    void(xy, n, m)


def _validate_visible(visible: Any, n: int, m: int) -> None:
    """Validate per-keypoint visibility mask.

    Expects a 2D bool ``np.ndarray`` with shape ``(n, m)``.

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Add the object dimension for a single instance: xy=points[np.newaxis, :, :2].
  2. Keep last dim as 2 or 3; drop extra channels: xy=xy[..., :3].
  3. Prefer KeyPoints.from_inference(...) / from_ultralytics connectors that normalize shape.
  4. Verify xy.shape == (num_people, num_keypoints, 2 or 3) with an assert before construction.

Example fix

# before
kp = KeyPoints(xy=points)  # points.shape == (17, 3) -> ValueError

# after
kp = KeyPoints(xy=points[np.newaxis, ...])  # (1, 17, 3)
Defensive patterns

Strategy: type-guard

Validate before calling

xy = np.asarray(xy)
if xy.ndim == 2:
    xy = xy[np.newaxis, ...]
xy = xy[..., :3] if xy.shape[-1] > 3 else xy
assert xy.ndim == 3 and xy.shape[-1] in (2, 3)
kp = KeyPoints(xy=xy)

Type guard

def is_valid_keypoint_xy(xy: Any) -> bool:
    return (
        isinstance(xy, np.ndarray)
        and xy.ndim == 3
        and xy.shape[2] in (2, 3)
    )

Prevention

When it happens

Trigger: Passing xy of shape (m, 2) for a single object (missing the batch dimension), a 2D flat array of all points, or an array with last dimension 4 (x, y, z, visibility).

Common situations: Wrapping raw pose-model keypoints without adding the object axis; using a 4-value-per-point format from a custom dataset; iterating per-detection and passing a (m, 2) slice directly to KeyPoints.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/1aa26f3fe3a4eb0f. Report an issue: GitHub.