roboflow/supervision · error · ValueError
Cannot filter keypoints with a 2D boolean mask where rows ha
Error message
Cannot filter keypoints with a 2D boolean mask where rows have different numbers of True values. All objects must select the same number of keypoints. Got counts per object: {counts.tolist()} What it means
sv.F1Score.update() accepts a single (predictions, targets) pair or equal-length lists (one per image) and raises when the counts differ. Entries are matched index-wise, so unequal lists mean images on one side have no counterpart on the other and the metric would silently misattribute everything after the first gap.
Source
Thrown at src/supervision/key_points/core.py:876
Raises:
ValueError: If `mask.shape[0]` does not match the number of objects, if
`mask.shape[1]` does not match the number of keypoints, or if
different rows of the mask select different numbers of `True` values.
"""
n = len(self.xy)
if mask.shape[0] != n:
raise ValueError(
f"2D boolean mask row count {mask.shape[0]} does not match "
f"object count {n}."
)
if mask.shape[1] != self.xy.shape[1]:
raise ValueError(
f"2D boolean mask column count {mask.shape[1]} does not match "
f"keypoint count {self.xy.shape[1]}."
)
counts = np.sum(mask, axis=1)
if n > 0 and not np.all(counts == counts[0]):
raise ValueError(
"Cannot filter keypoints with a 2D boolean mask where rows have "
"different numbers of True values. "
"All objects must select the same number of keypoints. "
f"Got counts per object: {counts.tolist()}"
)
k = int(counts[0]) if n > 0 else 0
xy_selected = np.zeros((n, k, self.xy.shape[2]), dtype=self.xy.dtype)
keypoint_confidence_selected: npt.NDArray[np.float32] | None = None
if self.keypoint_confidence is not None:
keypoint_confidence_selected = cast(
npt.NDArray[np.float32],
np.zeros((n, k), dtype=self.keypoint_confidence.dtype),
)
visible_selected: npt.NDArray[np.bool_] | None = None
if self.visible is not None:
visible_selected = np.zeros((n, k), dtype=bool)
for row in range(n):
row_indices = np.flatnonzero(mask[row])View on GitHub (pinned to 7f254d9784)
Solutions
- Append to both lists unconditionally; use sv.Detections.empty() for frames with no detections
- Iterate with zip(image, ground_truth) so both sides stay paired
- assert len(predictions_list) == len(targets_list) before update()
- Call update() once per image with single Detections objects instead of accumulating lists
Example fix
# before
for img, gt in zip(images, gts):
det = detector(img)
if det is not None:
preds.append(det)
targets.append(gt) # lists desync
f1.update(predictions=preds, targets=targets) # -> ValueError
# after
for img, gt in zip(images, gts):
det = detector(img) or sv.Detections.empty()
preds.append(det)
targets.append(gt)
f1.update(predictions=preds, targets=targets) Defensive patterns
Strategy: validation
Validate before calling
assert len(predictions) == len(targets), (
f'predictions ({len(predictions)}) and targets ({len(targets)}) must pair 1:1'
)
f1.update(predictions=predictions, targets=targets) Type guard
def is_paired_batch(predictions: list, targets: list) -> bool:
"""True when both lists are equal-length lists of Detections."""
return len(predictions) == len(targets) and all(
isinstance(p, sv.Detections) and isinstance(t, sv.Detections)
for p, t in zip(predictions, targets)
) Prevention
- Append sv.Detections.empty() for empty frames so lists never desync
- Drive both appends from the same loop iteration over the dataset
When it happens
Trigger: Calling f1.update(predictions=[...], targets=[...]) with mismatched list lengths — typically from loops that conditionally append to one list (e.g. skipping frames where the model found nothing) but not the other.
Common situations: Batch evaluation scripts appending predictions only for non-empty frames; dataset sweeps where some frames error out on one side; refactoring that moved one append inside an if-block.
Related errors
- edges is a dict but class_id is None; KeyPoints must have cl
- No edges defined for class_id={class_id}.
- 2D boolean mask row count {mask.shape[0]} does not match obj
- 2D boolean mask column count {mask.shape[1]} does not match
- Value must be a np.ndarray or a list
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/110030eaa00ff9dd.
Report an issue: GitHub.