roboflow/supervision · error · ValueError
class_id is required for CreateML export, but the provided D
Error message
class_id is required for CreateML export, but the provided Detections has class_id=None.
What it means
Raised by detections_to_createml_annotations when detections.class_id is None. CreateML annotations label every box via classes[class_id], so a class-less Detections cannot be exported. Unlike the COCO path (per-detection check), this check is on the whole array up front.
Source
Thrown at src/supervision/dataset/formats/createml.py:264
```pycon
>>> import numpy as np
>>> import supervision as sv
>>> from supervision.dataset.formats.createml import (
... detections_to_createml_annotations,
... )
>>> detections = sv.Detections(
... xyxy=np.array([[40, 40, 60, 60]], dtype=np.float32),
... class_id=np.array([0], dtype=int),
... )
>>> detections_to_createml_annotations(detections, classes=["dog"])
[{'label': 'dog', 'coordinates':
{'x': 50.0, 'y': 50.0, 'width': 20.0, 'height': 20.0}}]
```
"""
class_ids = detections.class_id
if class_ids is None:
raise ValueError(
"class_id is required for CreateML export, but the provided "
"Detections has class_id=None."
)
annotations: list[CreateMLDict] = []
for xyxy, class_id in zip(detections.xyxy, class_ids):
x_min, y_min, x_max, y_max = (float(value) for value in xyxy)
annotations.append(
{
"label": classes[int(class_id)],
"coordinates": {
"x": (x_min + x_max) / 2,
"y": (y_min + y_max) / 2,
"width": x_max - x_min,
"height": y_max - y_min,
},
}
)
return annotationsView on GitHub (pinned to 7f254d9784)
Solutions
- Attach class_id when constructing Detections: sv.Detections(xyxy=boxes, class_id=np.zeros(len(boxes), dtype=int)).
- Re-run inference with a multi-class model that emits class_id.
- Guard before export: if detections.class_id is None: raise your own error with context.
Example fix
// before detections = sv.Detections(xyxy=boxes) detections_to_createml_annotations(detections, classes=["dog"]) // after detections = sv.Detections(xyxy=boxes, class_id=np.zeros(len(boxes), dtype=int)) detections_to_createml_annotations(detections, classes=["dog"])
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), # single-class default
) Type guard
def has_class_id(dets: sv.Detections) -> bool:
"""True when class_id is present and index-aligned with xyxy."""
return dets.class_id is not None and len(dets.class_id) == len(dets.xyxy) Try / catch
try:
save_createml_annotations(dataset=ds, annotation_path=p)
except ValueError as exc:
if "class_id" in str(exc):
raise RuntimeError("Detections are class-agnostic; assign class ids first") from exc
raise Prevention
- Always pair xyxy with class_id when building Detections.
- Centralize Detections construction in one factory that enforces class_id presence.
- For class-agnostic models, define a single class and emit zeros.
When it happens
Trigger: Building sv.Detections(xyxy=..., confidence=...) without class_id, then calling detections_to_createml_annotations or save_createml_annotations (DetectionDataset.as_createml).
Common situations: Detector outputs that omit class_id (class-agnostic NMS); custom box lists for dataset conversion; slicing/filtering Detections and dropping the class_id field.
Related errors
- Detections must include class_id for COCO export.
- Detections must include class_id for Pascal VOC export.
- Detections must have class_id attribute.
- Detections class_id must be an integer for Pascal VOC export
- Class ID is required for YOLO annotations.
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/a4fd66d04d4ee4c6.
Report an issue: GitHub.