roboflow/supervision · error · ValueError

Detection annotation for image

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.

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.

Appendix: 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)