roboflow/supervision · error · ValueError

Detections must have class_id attribute.

Error message

Detections must have class_id attribute.

What it means

Raised by map_detections_class_id() when detections.class_id is None. The function's whole job is to remap class ids from a source dataset's indexing to a target dataset's indexing via np.vectorize over the class_id array; without class_id there is nothing to map and silently returning unchanged detections would hide the mistake.

Source

Thrown at src/supervision/dataset/utils.py:127

    index_mapping = {}

    for i, class_name in enumerate(source_classes):
        if class_name not in target_classes:
            raise ValueError(
                f"Class {class_name} not found in target classes. "
                "source_classes must be a subset of target_classes."
            )
        corresponding_index = target_classes.index(class_name)
        index_mapping[i] = corresponding_index

    return index_mapping


def map_detections_class_id(
    source_to_target_mapping: dict[int, int], detections: Detections
) -> Detections:
    if detections.class_id is None:
        raise ValueError("Detections must have class_id attribute.")
    if set(np.unique(detections.class_id)) - set(source_to_target_mapping.keys()):
        raise ValueError(
            "Detections class_id must be a subset of source_to_target_mapping keys."
        )

    detections_copy = copy.deepcopy(detections)

    if len(detections) > 0:
        detections_copy.class_id = np.vectorize(source_to_target_mapping.get)(
            detections_copy.class_id
        )

    return detections_copy


def check_no_basename_collisions(
    image_paths: list[str],
    key: Callable[[str], str],

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Ensure the upstream model/connector populates class_id (pass class ids when constructing Detections).
  2. If the detections are genuinely single-class, assign a placeholder class_id (e.g. np.zeros(len(detections), dtype=np.int64)) before mapping.
  3. Skip the remap for detections without class_id if your pipeline permits.

Example fix

// before
dets = sv.Detections(xyxy=boxes, confidence=scores)
mapped = map_detections_class_id(mapping, dets)  # ValueError

// after
dets = sv.Detections(xyxy=boxes, confidence=scores, class_id=np.zeros(len(boxes), dtype=np.int64))
mapped = map_detections_class_id(mapping, dets)
Defensive patterns

Strategy: validation

Validate before calling

if detections.class_id is None:
    detections = replace(
        detections,
        class_id=np.zeros(len(detections), dtype=np.int64),
    )
mapped = map_detections_class_id(mapping, detections)

Type guard

def has_class_id(dets: sv.Detections) -> bool:
    return dets.class_id is not None

Prevention

When it happens

Trigger: Calling map_detections_class_id(mapping, detections) on Detections built without class_id — e.g. sv.Detections(xyxy=boxes, confidence=scores) with no class_id, or output of a connector that does not emit class ids.

Common situations: Exporting/merging datasets where some annotations lack class labels; running detection with a class-agnostic model; forgetting class_id in a manually-constructed Detections during dataset conversion.

Related errors


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