roboflow/supervision · error · ValueError
F1Score metric requires `class_id` on both predictions and t
Error message
F1Score metric requires `class_id` on both predictions and targets.
What it means
Raised by F1Score.update() when both predictions and targets are present for an image, but either side is missing class_id. Class identity is required on both sides to decide whether a match is a true positive or a class-confused mismatch, and to group per-class statistics. The check runs before any IoU matching is done.
Source
Thrown at src/supervision/metrics/f1_score.py:215
continue
stats.append(
(
np.zeros(
(len(prediction_class_ids), iou_thresholds.size),
dtype=np.bool_,
),
np.zeros(
(len(prediction_class_ids), iou_thresholds.size),
dtype=np.bool_,
),
prediction_confidence,
prediction_class_ids,
np.zeros((0,), dtype=np.int32),
)
)
elif len(targets) > 0:
if predictions.class_id is None or targets.class_id is None:
raise ValueError(
"F1Score metric requires `class_id` on both predictions "
"and targets."
)
if len(predictions) == 0:
target_class_ids = np.asarray(targets.class_id, dtype=np.int32)[
target_size_mask
]
if len(target_class_ids) == 0:
continue
stats.append(
(
np.zeros((0, iou_thresholds.size), dtype=bool),
np.zeros((0, iou_thresholds.size), dtype=bool),
np.zeros((0,), dtype=np.float32),
np.zeros((0,), dtype=int),
target_class_ids,
)
)View on GitHub (pinned to 7f254d9784)
Solutions
- Set class_id on both Detections: np.array of int class ids aligned with xyxy rows
- If labels are strings, map them to integer ids first (e.g. via a {name: id} dict) before constructing Detections
- Prefer built-in loaders (DetectionDataset / from_* connectors) which always populate class_id
Example fix
# before
targets = sv.Detections(xyxy=gt_boxes) # class_id missing
f1.update(targets=targets, predictions=preds)
# after
targets = sv.Detections(
xyxy=gt_boxes,
class_id=gt_class_ids,
)
f1.update(targets=targets, predictions=preds) Defensive patterns
Strategy: validation
Validate before calling
if predictions.class_id is None or targets.class_id is None:
raise ValueError('Both targets and predictions need class_id for F1Score')
f1.update(targets=targets, predictions=predictions) Type guard
def both_classified(dets_a: sv.Detections, dets_b: sv.Detections) -> bool:
"""True when both Detections carry class_id."""
return dets_a.class_id is not None and dets_b.class_id is not None Try / catch
try:
f1.update(targets=targets, predictions=predictions)
except ValueError as e:
if 'class_id on both' in str(e):
# fill missing side with a single neutral class
if targets.class_id is None:
targets.class_id = np.zeros(len(targets), dtype=np.int32)
if predictions.class_id is None:
predictions.class_id = np.zeros(len(predictions), dtype=np.int32)
else:
raise Prevention
- Load ground truth with sv.DetectionDataset (COCO/YOLO/VOC) so class_id is always set
- Standardize a to_detections() helper in your pipeline that guarantees class_id
When it happens
Trigger: Calling F1Score().update() with predictions.class_id is None and len(targets) > 0, or with targets.class_id is None, on any image pair where both arrays are non-empty.
Common situations: Ground-truth Detections built manually for evaluation datasets that omit class_id; using detections from a segmentation model connector that only fills xyxy/mask; mixing connector outputs with different field conventions.
Related errors
- MeanAverageRecall metric requires `class_id` on both predict
- F1Score metric requires `class_id` and `confidence` on predi
- F1Score metric requires `confidence` on predictions.
- edges is a dict but class_id is None; KeyPoints must have cl
- 2D boolean mask row count {mask.shape[0]} does not match obj
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/10903b104eea2a92.
Report an issue: GitHub.