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

  1. Set class_id on both Detections: np.array of int class ids aligned with xyxy rows
  2. If labels are strings, map them to integer ids first (e.g. via a {name: id} dict) before constructing Detections
  3. 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

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


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