{"record":{"id":"892ec06dc6596f13","repo":"roboflow/supervision","slug":"detections-class-id-must-be-an-integer-for-pascal","errorCode":null,"errorMessage":"Detections class_id must be an integer for Pascal VOC export, got {type(class_id)}.","messagePattern":"Detections class_id must be an integer for Pascal VOC export, got (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/dataset/formats/pascal_voc.py","lineNumber":163,"sourceCode":"    # Add size element\n    size = SubElement(annotation, \"size\")\n    w = SubElement(size, \"width\")\n    w.text = str(width)\n    h = SubElement(size, \"height\")\n    h.text = str(height)\n    d = SubElement(size, \"depth\")\n    d.text = str(depth)\n\n    # Add segmented element\n    segmented = SubElement(annotation, \"segmented\")\n    segmented.text = \"0\"\n\n    # Add object elements\n    for xyxy, mask, _, class_id, _, _ in detections:\n        if class_id is None:\n            raise ValueError(\"Detections must include class_id for Pascal VOC export.\")\n        if not isinstance(class_id, (int, np.integer)):\n            raise ValueError(\n                f\"Detections class_id must be an integer for Pascal VOC export, \"\n                f\"got {type(class_id)!r}.\"\n            )\n        name = classes[class_id]\n        if mask is not None:\n            polygons = approximate_mask_with_polygons(\n                mask=mask,\n                min_image_area_percentage=min_image_area_percentage,\n                max_image_area_percentage=max_image_area_percentage,\n                approximation_percentage=approximation_percentage,\n            )\n            for polygon in polygons:\n                xyxy = polygon_to_xyxy(polygon=polygon)\n                next_object = object_to_pascal_voc(\n                    xyxy=xyxy, name=name, polygon=polygon\n                )\n                annotation.append(next_object)\n        else:","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/dataset/formats/pascal_voc.py#L145-L181","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Cast to integer dtype when building Detections: class_id=np.asarray(raw_ids, dtype=int).","After .tolist() on a float tensor, re-wrap with np.array(..., dtype=int).","Verify detections.class_id.dtype.kind == 'i' before export."],"exampleFix":"// before\ndetections = sv.Detections(xyxy=boxes, class_id=np.array([0.0, 1.0]))\n\n// after\ndetections = sv.Detections(xyxy=boxes, class_id=np.array([0.0, 1.0], dtype=int))","handlingStrategy":"type-guard","validationCode":"raw_ids = [0.0, 1.0]  # e.g. from JSON\ndetections = sv.Detections(\n    xyxy=boxes,\n    class_id=np.asarray(raw_ids, dtype=int),  # coerce floats to int up front\n)","typeGuard":"def class_id_is_integer(dets: sv.Detections) -> bool:\n    \"\"\"True when class_id exists and has an integer dtype.\"\"\"\n    return dets.class_id is not None and dets.class_id.dtype.kind in (\"i\", \"u\")","tryCatchPattern":"try:\n    save_pascal_voc_annotations(dataset=ds, annotations_directory_path=out_dir)\nexcept ValueError as exc:\n    if \"must be an integer\" in str(exc):\n        raise TypeError(\"Coerce class_id to int dtype before VOC export\") from exc\n    raise","preventionTips":["Always build class_id with an explicit integer dtype: np.array(ids, dtype=int).","After tensor.tolist() on float tensors, re-wrap with dtype=int.","Watch for dtype upcast to float when concatenating class_id with float arrays."],"tags":["pascal-voc","dtype","class-id","dataset-export"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}