{"record":{"id":"1aa26f3fe3a4eb0f","repo":"roboflow/supervision","slug":"xy-must-be-a-3d-np-ndarray-with-shape-expected-sh","errorCode":null,"errorMessage":"xy must be a 3D np.ndarray with shape {expected_shape}, but got shape {actual_shape}","messagePattern":"xy must be a 3D np\\.ndarray with shape (.+?), but got shape (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/validators/__init__.py","lineNumber":221,"sourceCode":"        else:\n            raise ValueError(f\"Value for key '{key}' must be a list or np.ndarray\")\n\n\n@deprecated(  # type: ignore[untyped-decorator]\n    target=_validate_data,\n    deprecated_in=\"0.29.0\",\n    remove_in=\"0.32.0\",\n)\ndef validate_data(data: dict[str, Any], n: int) -> None:\n    void(data, n)\n\n\ndef _validate_xy(xy: Any, n: int, m: int) -> None:\n    expected_shape = f\"({n}, {m}, 2) or ({n}, {m}, 3)\"\n    actual_shape = str(getattr(xy, \"shape\", None))\n\n    if not isinstance(xy, np.ndarray) or xy.ndim != 3 or xy.shape[2] not in (2, 3):\n        raise ValueError(\n            f\"xy must be a 3D np.ndarray with shape {expected_shape}, but got shape \"\n            f\"{actual_shape}\"\n        )\n\n\n@deprecated(  # type: ignore[untyped-decorator]\n    target=_validate_xy,\n    deprecated_in=\"0.29.0\",\n    remove_in=\"0.32.0\",\n)\ndef validate_xy(xy: Any, n: int, m: int) -> None:\n    void(xy, n, m)\n\n\ndef _validate_visible(visible: Any, n: int, m: int) -> None:\n    \"\"\"Validate per-keypoint visibility mask.\n\n    Expects a 2D bool ``np.ndarray`` with shape ``(n, m)``.","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/validators/__init__.py#L203-L239","documentation":"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).","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before\nkp = KeyPoints(xy=points)  # points.shape == (17, 3) -> ValueError\n\n# after\nkp = KeyPoints(xy=points[np.newaxis, ...])  # (1, 17, 3)","handlingStrategy":"type-guard","validationCode":"xy = np.asarray(xy)\nif xy.ndim == 2:\n    xy = xy[np.newaxis, ...]\nxy = xy[..., :3] if xy.shape[-1] > 3 else xy\nassert xy.ndim == 3 and xy.shape[-1] in (2, 3)\nkp = KeyPoints(xy=xy)","typeGuard":"def is_valid_keypoint_xy(xy: Any) -> bool:\n    return (\n        isinstance(xy, np.ndarray)\n        and xy.ndim == 3\n        and xy.shape[2] in (2, 3)\n    )","tryCatchPattern":null,"preventionTips":["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."],"tags":["keypoints","pose","validation","shape"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}