{"record":{"id":"ed5efb3e477bdeda","repo":"roboflow/supervision","slug":"class-id-is-required-for-yolo-annotations","errorCode":null,"errorMessage":"Class ID is required for YOLO annotations.","messagePattern":"Class ID is required for YOLO annotations\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/dataset/formats/yolo.py","lineNumber":387,"sourceCode":"            \"`detections.data` with shape (N, 4, 2). Load OBB datasets via \"\n            \"`DetectionDataset.from_yolo(..., is_obb=True)` or set \"\n            f\"`detections.data['{ORIENTED_BOX_COORDINATES}']` \"\n            \"(shape (N, 4, 2)) before exporting.\"\n        )\n\n    if is_obb and detections.mask is not None:\n        warnings.warn(\n            \"`detections.mask` is ignored when `is_obb=True`; \"\n            \"OBB annotations use corner coordinates from \"\n            f\"`detections.data['{ORIENTED_BOX_COORDINATES}']`.\",\n            UserWarning,\n            stacklevel=2,\n        )\n\n    annotation: list[str] = []\n    for xyxy, mask, _, class_id, _, data in detections:\n        if class_id is None:\n            raise ValueError(\"Class ID is required for YOLO annotations.\")\n        if not isinstance(class_id, (int, np.integer)):\n            raise ValueError(\n                f\"Detections class_id must be an integer for YOLO export, \"\n                f\"got {type(class_id)!r}.\"\n            )\n        class_id_int = int(class_id)\n\n        if is_obb:\n            corners = np.asarray(data[ORIENTED_BOX_COORDINATES], dtype=np.float32)\n            if corners.shape != (4, 2):\n                raise ValueError(\n                    f\"OBB data for each detection must have shape (4, 2), \"\n                    f\"got {corners.shape}. Ensure \"\n                    f\"`detections.data['{ORIENTED_BOX_COORDINATES}']` has \"\n                    \"shape (N, 4, 2) before exporting.\"\n                )\n            next_object = object_to_yolo(\n                xyxy=xyxy,","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/dataset/formats/yolo.py#L369-L405","documentation":"Raised by detections_to_yolo_annotations when iterating detections and a detection's class_id is None. YOLO annotation lines start with a class index, so a class-less Detections (e.g. from a tracker that dropped class_id or a manually built empty-class Detections) cannot be serialized.","triggerScenarios":"Calling sv.detections_to_yolo_annotations(...) (or as_yolo_annotations on a dataset) where detections.class_id is None — commonly Detections created with only xyxy, or a class_id array that was deliberately cleared.","commonSituations":"Building Detections from raw model output without wiring class ids; using tracker output where class_id was not propagated; slicing/filtering detections and losing class_id; testing code with minimal Detections(xyxy=...) constructions.","solutions":["Attach class ids when constructing: Detections(xyxy=..., class_id=np.array([0, 1], dtype=int)).","If classes are genuinely unknown, assign a placeholder class (e.g. zeros) before export.","Check any intermediate step (tracker, smoother, filter) that returns class_id=None and re-attach classes by tracker_id mapping."],"exampleFix":"# before\ndets = sv.Detections(xyxy=xyxy)  # no class_id\nlines = sv.detections_to_yolo_annotations(dets, image_shape=shape)\n# after\ndets = sv.Detections(xyxy=xyxy, class_id=np.zeros(len(xyxy), dtype=int))\nlines = sv.detections_to_yolo_annotations(dets, image_shape=shape)","handlingStrategy":"type-guard","validationCode":"import numpy as np\n\ndef ready_for_yolo_export(detections) -> bool:\n    \"\"\"YOLO export needs a non-None class_id array.\"\"\"\n    return detections.class_id is not None","typeGuard":"def has_class_id(detections) -> bool:\n    \"\"\"True when every detection carries an integer class id.\"\"\"\n    return detections.class_id is not None and detections.class_id.dtype.kind in 'iu'","tryCatchPattern":"try:\n    lines = sv.detections_to_yolo_annotations(dets, image_shape=shape)\nexcept ValueError as e:\n    if 'Class ID is required' in str(e):\n        dets.class_id = np.zeros(len(dets), dtype=np.int64)  # placeholder class\n        lines = sv.detections_to_yolo_annotations(dets, image_shape=shape)\n    else:\n        raise","preventionTips":["Always construct Detections with class_id when the output will be serialized.","Check trackers/filters preserve class_id before export steps.","For class-agnostic models, use a single-class id of 0."],"tags":["yolo","export","detections","class-id"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}