roboflow/supervision · error · ValueError

All KeyPoints must have the same number of keypoints per ske

Error message

All KeyPoints must have the same number of keypoints per skeleton to be merged; got counts {sorted(keypoint_counts)}.

What it means

When sv.F1Score is constructed with metric_target=MetricTarget.MASKS, _detections_content() returns detections.mask (dense bool array or CompactMask). If mask is None on a non-empty Detections object, mask IoU cannot be computed and this error is raised. Empty Detections without masks are allowed and get a (0,0,0) placeholder.

Source

Thrown at src/supervision/key_points/core.py:1261

            key_points for key_points in key_points_list if not key_points.is_empty()
        ]

        if len(key_points_list) == 0:
            return cls.empty()

        for key_points in key_points_list:
            _validate_keypoints_fields(
                xy=key_points.xy,
                class_id=key_points.class_id,
                confidence=key_points.keypoint_confidence,
                detection_confidence=key_points.detection_confidence,
                visible=key_points.visible,
                data=key_points.data,
            )

        keypoint_counts = {key_points.xy.shape[1] for key_points in key_points_list}
        if len(keypoint_counts) > 1:
            raise ValueError(
                "All KeyPoints must have the same number of keypoints per "
                f"skeleton to be merged; got counts {sorted(keypoint_counts)}."
            )

        keypoint_depths = {key_points.xy.shape[2] for key_points in key_points_list}
        if len(keypoint_depths) > 1:
            raise ValueError(
                "All KeyPoints must have the same coordinate depth per "
                f"skeleton to be merged; got depths {sorted(keypoint_depths)}."
            )

        xy = np.vstack([key_points.xy for key_points in key_points_list])

        def stack_or_none(name: str) -> npt.NDArray[np.generic] | None:
            values = [getattr(key_points, name) for key_points in key_points_list]
            if all(value is None for value in values):
                return None
            if any(value is None for value in values):

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Feed Detections from a segmentation connector (e.g. from_ultralytics on YOLO-seg output)
  2. Pass mask=np.array((N,H,W), bool) when constructing Detections manually
  3. Keep metric_target=MetricTarget.BOXES if you only have boxes
  4. Ensure both predictions and targets carry masks — the check applies to each Detections passed in

Example fix

# before
f1 = sv.F1Score(metric_target=sv.MetricTarget.MASKS)
det = sv.Detections(
    xyxy=np.array([[30.0, 30.0, 100.0, 100.0]]),
    class_id=np.array([0]),
    confidence=np.array([0.9]),
)
f1.update(predictions=[det], targets=[gt])  # -> ValueError

# after
masks = np.zeros((1, 480, 640), dtype=bool)
masks[0, 30:100, 30:100] = True
det = sv.Detections(
    xyxy=np.array([[30.0, 30.0, 100.0, 100.0]]),
    class_id=np.array([0]),
    confidence=np.array([0.9]),
    mask=masks,
)
f1.update(predictions=[det], targets=[gt])
Defensive patterns

Strategy: type-guard

Validate before calling

def masks_ready(detections: sv.Detections) -> bool:
    return detections.is_empty() or detections.mask is not None

for d in predictions + targets:
    assert masks_ready(d), 'MASKS F1 requires non-empty Detections to carry mask'

Type guard

def is_mask_detections(detections: sv.Detections) -> bool:
    """True when Detections can be evaluated with MetricTarget.MASKS."""
    return detections.is_empty() or detections.mask is not None

Try / catch

try:
    f1_mask.update(predictions=preds, targets=gts)
except ValueError as e:
    if 'requires detections to include masks' in str(e):
        logger.warning('No masks found; falling back to BOXES F1')
        f1_box = sv.F1Score()  # and re-run
    else:
        raise

Prevention

When it happens

Trigger: sv.F1Score(metric_target=sv.MetricTarget.MASKS) fed with box-only Detections: manual sv.Detections(xyxy=...) without mask=, box-detector connectors, or COCO detection (non-segmentation) annotations.

Common situations: Toggling an existing box-metrics script to MASKS without switching the model or dataset to segmentation sources; manual Detections construction in tests that omit mask.

Related errors


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