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
- Ensure the upstream model/connector populates class_id (pass class ids when constructing Detections).
- If the detections are genuinely single-class, assign a placeholder class_id (e.g. np.zeros(len(detections), dtype=np.int64)) before mapping.
- 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
- Ensure model connectors emit class_id before dataset export workflows.
- Assign a default class_id (zeros) for class-agnostic pipelines that later remap classes.
- Check detections.class_id is not None before any class-mapping call.
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
- Detections class_id must be a subset of source_to_target_map
- Detection annotation for image {image_path} contains non-int
- Detection annotation for image {image_path} contains class_i
- KeyPoints class_id must be given for NMS to be executed. If
- Class {class_name} not found in target classes. source_class
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/87d1ce725b958f1e.
Report an issue: GitHub.