roboflow/supervision · error · ValueError

Detections must include class_id for Pascal VOC export.

Error message

Detections must include class_id for Pascal VOC export.

What it means

Raised by detections_to_pascal_voc while iterating Detections when a detection's class_id is None. VOC XML requires a <name> element derived from classes[class_id] for every object, so class-less detections cannot be serialized.

Source

Thrown at src/supervision/dataset/formats/pascal_voc.py:161

    database.text = "roboflow.ai"

    # Add size element
    size = SubElement(annotation, "size")
    w = SubElement(size, "width")
    w.text = str(width)
    h = SubElement(size, "height")
    h.text = str(height)
    d = SubElement(size, "depth")
    d.text = str(depth)

    # Add segmented element
    segmented = SubElement(annotation, "segmented")
    segmented.text = "0"

    # Add object elements
    for xyxy, mask, _, class_id, _, _ in detections:
        if class_id is None:
            raise ValueError("Detections must include class_id for Pascal VOC export.")
        if not isinstance(class_id, (int, np.integer)):
            raise ValueError(
                f"Detections class_id must be an integer for Pascal VOC export, "
                f"got {type(class_id)!r}."
            )
        name = classes[class_id]
        if mask is not None:
            polygons = approximate_mask_with_polygons(
                mask=mask,
                min_image_area_percentage=min_image_area_percentage,
                max_image_area_percentage=max_image_area_percentage,
                approximation_percentage=approximation_percentage,
            )
            for polygon in polygons:
                xyxy = polygon_to_xyxy(polygon=polygon)
                next_object = object_to_pascal_voc(
                    xyxy=xyxy, name=name, polygon=polygon
                )

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Construct Detections with class_id: sv.Detections(xyxy=boxes, class_id=ids).
  2. For single-class exports, use class_id=np.zeros(len(boxes), dtype=int) and classes=["object"].
  3. Assert detections.class_id is not None before export.

Example fix

// before
detections = sv.Detections(xyxy=boxes)

// after
detections = sv.Detections(xyxy=boxes, class_id=np.zeros(len(boxes), dtype=int))
Defensive patterns

Strategy: type-guard

Validate before calling

if detections.class_id is None:
    detections = sv.Detections(
        xyxy=detections.xyxy,
        confidence=detections.confidence,
        class_id=np.zeros(len(detections), dtype=int),
    )

Type guard

def has_class_id(dets: sv.Detections) -> bool:
    """True when class_id is present for every detection."""
    return dets.class_id is not None and len(dets.class_id) == len(dets)

Try / catch

try:
    save_pascal_voc_annotations(dataset=ds, annotations_directory_path=out_dir)
except ValueError as exc:
    if "class_id" in str(exc):
        raise RuntimeError("Cannot write VOC XML without class ids") from exc
    raise

Prevention

When it happens

Trigger: Calling save_pascal_voc_annotations on a DetectionDataset whose Detections were built without class_id, or calling detections_to_pascal_voc directly with sv.Detections(xyxy=..., confidence=...).

Common situations: Class-agnostic detector outputs; custom box lists assembled for dataset conversion; Detections filtered or transformed in a way that drops class_id.

Related errors


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