roboflow/supervision · error · ValueError

class_id is required for LabelMe export, but the provided De

Error message

class_id is required for LabelMe export, but the provided Detections has class_id=None.

What it means

Raised by detections_to_labelme_shapes when detections.class_id is None. LabelMe shapes need a text label looked up from the classes list by class index, so detections without class ids cannot be exported. This mirrors the YOLO export requirement but for the LabelMe format.

Source

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

    Masked detections are exported as ``polygon`` shapes (one per connected
    component); box-only detections — and masked detections whose mask yields no
    polygon contour (e.g. an empty or sub-pixel mask) — are exported as
    ``rectangle`` shapes, so no detection is silently dropped.

    Args:
        detections: The detections to export.
        classes: List of class names indexed by ``class_id``.

    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 = []

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Provide class ids when constructing the Detections: Detections(xyxy=..., class_id=np.array([0], dtype=int)).
  2. If the detector is class-agnostic, fill class_id with zeros and pass a single-class classes list.
  3. Re-attach class ids from a previous Detections instance before export (e.g. by index or tracker_id).

Example fix

# before
dets = sv.Detections(xyxy=xyxy)
shapes = sv.detections_to_labelme_shapes(dets, classes=['cat'])
# after
dets = sv.Detections(xyxy=xyxy, class_id=np.zeros(len(xyxy), dtype=int))
shapes = sv.detections_to_labelme_shapes(dets, classes=['cat'])
Defensive patterns

Strategy: type-guard

Validate before calling

import numpy as np

def ready_for_labelme_export(detections) -> bool:
    """LabelMe export requires a non-None class_id array."""
    return detections.class_id is not None

Type guard

def has_class_id(detections) -> bool:
    """True when detections carry a class_id array."""
    return detections.class_id is not None

Try / catch

try:
    shapes = sv.detections_to_labelme_shapes(dets, classes=classes)
except ValueError as e:
    if 'class_id is required for LabelMe' in str(e):
        dets.class_id = np.zeros(len(dets), dtype=np.int64)
        shapes = sv.detections_to_labelme_shapes(dets, classes=classes)
    else:
        raise

Prevention

When it happens

Trigger: Calling sv.detections_to_labelme_shapes(detections, classes=[...]) on Detections constructed without class_id (only xyxy/confidence), or after a processing step that dropped class_id.

Common situations: Exporting results of a class-agnostic detector (class_id None by design); building Detections from geometric zones or manual boxes for dataset creation; losing class_id through custom filtering code.

Related errors


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