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
- Add the object dimension for a single instance: xy=points[np.newaxis, :, :2].
- Keep last dim as 2 or 3; drop extra channels: xy=xy[..., :3].
- Prefer KeyPoints.from_inference(...) / from_ultralytics connectors that normalize shape.
- 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
- Single object: add np.newaxis before constructing KeyPoints.
- Know your skeleton's per-point format (2 vs 3 channels).
- Use KeyPoints.from_* connectors for supported pose models.
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
- visible must be a 2D np.ndarray with shape (n, m), but got s
- visible first dimension must be {n}, but got shape {actual_s
- visible second dimension must be {m}, but got shape {actual_
- keypoint_confidence must be a 2D np.ndarray with shape (n, m
- keypoint_confidence first dimension must be {n}, but got sha
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/1aa26f3fe3a4eb0f.
Report an issue: GitHub.