roboflow/supervision · error · ValueError

class_id {class_index} at detection index {index} is out of

Error message

class_id {class_index} at detection index {index} is out of range for classes list of length {len(classes)}.

What it means

Raised during LabelMe export when a detection's class_id indexes outside the supplied classes list (negative or >= len(classes)). Labels are looked up as classes[class_index], so an out-of-range id has no name and the shape cannot be written; the message includes the offending id, the detection index, and the list length.

Source

Thrown at src/supervision/dataset/formats/labelme.py:333

    Returns:
        A list of LabelMe shape dicts ready to embed in a ``.json`` annotation.

    Raises:
        ValueError: If ``detections.class_id`` is ``None`` or if any
            ``class_id`` value is out of range for ``classes``.
    """
    class_ids = detections.class_id
    if class_ids is None:
        raise ValueError(
            "class_id is required for LabelMe export, but the provided "
            "Detections has class_id=None."
        )
    masks = detections.mask
    shapes: list[LabelMeDict] = []
    for index in range(len(detections)):
        class_index = int(class_ids[index])
        if class_index < 0 or class_index >= len(classes):
            raise ValueError(
                f"class_id {class_index} at detection index {index} is out of "
                f"range for classes list of length {len(classes)}."
            )
        label = classes[class_index]
        if masks is not None:
            mask_arr = np.asarray(masks[index], dtype=np.bool_)
            polygons = mask_to_polygons(mask_arr)
        else:
            polygons = []
        if polygons:
            for polygon in polygons:
                points = [[float(x), float(y)] for x, y in polygon]
                shapes.append(_build_shape(label, points, "polygon"))
        else:
            x_min, y_min, x_max, y_max = (
                float(value) for value in detections.xyxy[index]
            )
            points = [[x_min, y_min], [x_max, y_max]]

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Print detections.class_id.max() and len(classes) — max id must be <= len(classes)-1.
  2. Pass the full class-name list matching the model's id space.
  3. If you intentionally filtered classes, remap ids to the new contiguous indices before export (e.g. with a lookup array).
  4. Negative ids mean unset/garbage ids — reassign them before exporting.

Example fix

# before
classes = ['cat']  # model actually has 2 classes
shapes = sv.detections_to_labelme_shapes(dets, classes=classes)
# after
classes = ['cat', 'dog']
shapes = sv.detections_to_labelme_shapes(dets, classes=classes)
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np

def class_ids_in_range(detections, classes: list[str]) -> bool:
    """Every class_id must index into the classes list."""
    if detections.class_id is None:
        return False
    return bool(np.all((detections.class_id >= 0)
                       & (detections.class_id < len(classes))))

Try / catch

try:
    shapes = sv.detections_to_labelme_shapes(dets, classes=classes)
except ValueError as e:
    if 'out of range for classes' in str(e):
        raise SystemExit(f'Pass the full class list or remap ids: {e}') from e
    raise

Prevention

When it happens

Trigger: sv.detections_to_labelme_shapes(detections, classes=[...]) where detections.class_id contains e.g. 5 with only 3 class names — common after filtering a classes list or merging detections from different models.

Common situations: Passing a shortened classes list (e.g. only kept classes after filtering) while detections still carry original ids; model with more classes than the names list supplied; off-by-one confusion between class count and max index; class ids loaded from another dataset's mapping.

Related errors


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