roboflow/supervision · error · ValueError

Detection annotation for image {image_path} contains class_i

Error message

Detection annotation for image {image_path} contains class_id {int(invalid_class_ids[0])}, outside the valid range {valid_range} for {len(self.classes)} classes.

What it means

Raised by the DetectionDataset constructor when an annotation contains a class_id outside [0, len(classes)-1]. Right after this check the constructor does np_classes[class_ids] to attach class-name metadata; an out-of-range id would raise a cryptic IndexError instead, so it validates first and reports the offending image, id, and valid range. An empty classes list yields 'empty' as the valid range.

Source

Thrown at src/supervision/dataset/core.py:129

            class_ids = annotation.class_id
            if class_ids is None:
                continue
            if not np.issubdtype(class_ids.dtype, np.integer):
                raise ValueError(
                    f"Detection annotation for image {image_path!r} contains "
                    f"non-integer class_id values with dtype {class_ids.dtype}."
                )

            invalid_class_ids = class_ids[
                (class_ids < 0) | (class_ids >= len(self.classes))
            ]
            if len(invalid_class_ids) > 0:
                valid_range = (
                    "empty"
                    if len(self.classes) == 0
                    else f"[0, {len(self.classes) - 1}]"
                )
                raise ValueError(
                    f"Detection annotation for image {image_path!r} contains "
                    f"class_id {int(invalid_class_ids[0])}, outside the valid "
                    f"range {valid_range} for {len(self.classes)} classes."
                )

            annotation.data[CLASS_NAME_DATA_FIELD] = np_classes[class_ids]

        # Eliminate duplicates while preserving order
        self.image_paths = list(dict.fromkeys(images))

        self._images_in_memory: dict[str, npt.NDArray[np.uint8]] = {}
        if isinstance(images, dict):
            self._images_in_memory = images
            warn_deprecated(
                "Passing a `Dict[str, np.ndarray]` into `DetectionDataset` is "
                "deprecated in `0.30.0` and will be removed in `0.33.0`. Use "
                "a list of paths `List[str]` instead."
            )

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Remap raw category ids to contiguous 0-based indices using the classes list (the same logic as build_class_index_mapping) before constructing Detections.
  2. Extend the classes list so it covers every id present: classes must have at least max(class_id)+1 entries.
  3. Drop or fix annotations with ids beyond your class list.

Example fix

// before
classes = ['cat', 'dog']
dets = sv.Detections(xyxy=boxes, class_id=np.array([1, 3, 7]))  # 3, 7 out of range
ds = DetectionDataset(classes=classes, images=paths, annotations=anns)

// after
# remap sparse COCO-style ids to contiguous indices
cat_to_idx = {c: i for i, c in enumerate(['cat', 'dog', 'horse', ...])}
dets = sv.Detections(xyxy=boxes, class_id=np.array([cat_to_idx[c] for c in ['dog', 'horse', ...]]))
ds = DetectionDataset(classes=list(cat_to_idx), images=paths, annotations=anns)
Defensive patterns

Strategy: validation

Validate before calling

for path, dets in annotations.items():
    if dets.class_id is not None and len(dets.class_id) and (dets.class_id.max() >= len(classes) or dets.class_id.min() < 0):
        raise ValueError(f"{path}: class_id outside [0, {len(classes) - 1}]")
ds = DetectionDataset(classes=classes, images=images, annotations=annotations)

Type guard

def class_ids_in_range(dets: sv.Detections, num_classes: int) -> bool:
    if dets.class_id is None:
        return True
    return bool(np.all((dets.class_id >= 0) & (dets.class_id < num_classes)))

Prevention

When it happens

Trigger: Constructing DetectionDataset(classes=['cat','dog'], ...) with an annotation containing class_id=3 (valid range is [0,1]); or passing annotations with ids offset by 1; or building the dataset with an empty classes list while annotations carry ids.

Common situations: COCO datasets where category_id is not contiguous (e.g. ids 1,3,7 used directly without remapping to indices); merging datasets with different class orderings; forgetting that classes=[] invalidates every id; off-by-one from 1-based id schemes.

Related errors


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