roboflow/supervision · error · ValueError

Detections class_id must be an integer for Pascal VOC export

Error message

Detections class_id must be an integer for Pascal VOC export, got {type(class_id)}.

What it means

Raised by detections_to_pascal_voc when a detection's class_id is neither a Python int nor a NumPy integer. When iterating a Detections, unpacked per-detection values are normally np.int64, so this fires when class_id was stored as a float or other type — VOC writes <name> via classes[class_id], which requires integer indexing.

Source

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

    # 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
                )
                annotation.append(next_object)
        else:

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Cast to integer dtype when building Detections: class_id=np.asarray(raw_ids, dtype=int).
  2. After .tolist() on a float tensor, re-wrap with np.array(..., dtype=int).
  3. Verify detections.class_id.dtype.kind == 'i' before export.

Example fix

// before
detections = sv.Detections(xyxy=boxes, class_id=np.array([0.0, 1.0]))

// after
detections = sv.Detections(xyxy=boxes, class_id=np.array([0.0, 1.0], dtype=int))
Defensive patterns

Strategy: type-guard

Validate before calling

raw_ids = [0.0, 1.0]  # e.g. from JSON
detections = sv.Detections(
    xyxy=boxes,
    class_id=np.asarray(raw_ids, dtype=int),  # coerce floats to int up front
)

Type guard

def class_id_is_integer(dets: sv.Detections) -> bool:
    """True when class_id exists and has an integer dtype."""
    return dets.class_id is not None and dets.class_id.dtype.kind in ("i", "u")

Try / catch

try:
    save_pascal_voc_annotations(dataset=ds, annotations_directory_path=out_dir)
except ValueError as exc:
    if "must be an integer" in str(exc):
        raise TypeError("Coerce class_id to int dtype before VOC export") from exc
    raise

Prevention

When it happens

Trigger: class_id=np.array([0.0, 1.0]) (float dtype), class_id from a JSON-parsed list of floats, or a tensor converted with .tolist() on a float tensor, passed to save_pascal_voc_annotations / detections_to_pascal_voc.

Common situations: Loading class ids from JSON/YAML where they became floats; converting model logits/argmax outputs with dtype float32; concatenating arrays that upcast int to float.

Related errors


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