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
- Feed Detections from a segmentation connector (e.g. from_ultralytics on YOLO-seg output)
- Pass mask=np.array((N,H,W), bool) when constructing Detections manually
- Keep metric_target=MetricTarget.BOXES if you only have boxes
- 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
- Select MASKS only when model and annotations both provide segmentation masks
- Always pass mask= when hand-constructing Detections for mask metrics
- Check detections.mask is not None in batch loops and fail with a contextual error naming the image
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
- MeanAverageRecall with `MetricTarget.MASKS` requires detecti
- MeanAveragePrecision with `MetricTarget.MASKS` requires mask
- 2D boolean mask row count {mask.shape[0]} does not match obj
- 2D boolean mask column count {mask.shape[1]} does not match
- Cannot filter keypoints with a 2D boolean mask where rows ha
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/9ad8dee71dad4357.
Report an issue: GitHub.