{"record":{"id":"5b342e551c88a72f","repo":"roboflow/supervision","slug":"class-id-class-index-at-detection-index-index","errorCode":null,"errorMessage":"class_id {class_index} at detection index {index} is out of range for classes list of length {len(classes)}.","messagePattern":"class_id (.+?) at detection index (.+?) is out of range for classes list of length (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/dataset/formats/labelme.py","lineNumber":333,"sourceCode":"    Returns:\n        A list of LabelMe shape dicts ready to embed in a ``.json`` annotation.\n\n    Raises:\n        ValueError: If ``detections.class_id`` is ``None`` or if any\n            ``class_id`` value is out of range for ``classes``.\n    \"\"\"\n    class_ids = detections.class_id\n    if class_ids is None:\n        raise ValueError(\n            \"class_id is required for LabelMe export, but the provided \"\n            \"Detections has class_id=None.\"\n        )\n    masks = detections.mask\n    shapes: list[LabelMeDict] = []\n    for index in range(len(detections)):\n        class_index = int(class_ids[index])\n        if class_index < 0 or class_index >= len(classes):\n            raise ValueError(\n                f\"class_id {class_index} at detection index {index} is out of \"\n                f\"range for classes list of length {len(classes)}.\"\n            )\n        label = classes[class_index]\n        if masks is not None:\n            mask_arr = np.asarray(masks[index], dtype=np.bool_)\n            polygons = mask_to_polygons(mask_arr)\n        else:\n            polygons = []\n        if polygons:\n            for polygon in polygons:\n                points = [[float(x), float(y)] for x, y in polygon]\n                shapes.append(_build_shape(label, points, \"polygon\"))\n        else:\n            x_min, y_min, x_max, y_max = (\n                float(value) for value in detections.xyxy[index]\n            )\n            points = [[x_min, y_min], [x_max, y_max]]","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/dataset/formats/labelme.py#L315-L351","documentation":"Raised during LabelMe export when a detection's class_id indexes outside the supplied classes list (negative or >= len(classes)). Labels are looked up as classes[class_index], so an out-of-range id has no name and the shape cannot be written; the message includes the offending id, the detection index, and the list length.","triggerScenarios":"sv.detections_to_labelme_shapes(detections, classes=[...]) where detections.class_id contains e.g. 5 with only 3 class names — common after filtering a classes list or merging detections from different models.","commonSituations":"Passing a shortened classes list (e.g. only kept classes after filtering) while detections still carry original ids; model with more classes than the names list supplied; off-by-one confusion between class count and max index; class ids loaded from another dataset's mapping.","solutions":["Print detections.class_id.max() and len(classes) — max id must be <= len(classes)-1.","Pass the full class-name list matching the model's id space.","If you intentionally filtered classes, remap ids to the new contiguous indices before export (e.g. with a lookup array).","Negative ids mean unset/garbage ids — reassign them before exporting."],"exampleFix":"# before\nclasses = ['cat']  # model actually has 2 classes\nshapes = sv.detections_to_labelme_shapes(dets, classes=classes)\n# after\nclasses = ['cat', 'dog']\nshapes = sv.detections_to_labelme_shapes(dets, classes=classes)","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef class_ids_in_range(detections, classes: list[str]) -> bool:\n    \"\"\"Every class_id must index into the classes list.\"\"\"\n    if detections.class_id is None:\n        return False\n    return bool(np.all((detections.class_id >= 0)\n                       & (detections.class_id < len(classes))))","typeGuard":null,"tryCatchPattern":"try:\n    shapes = sv.detections_to_labelme_shapes(dets, classes=classes)\nexcept ValueError as e:\n    if 'out of range for classes' in str(e):\n        raise SystemExit(f'Pass the full class list or remap ids: {e}') from e\n    raise","preventionTips":["Keep the classes list in lockstep with the model's id space.","When filtering classes, remap detection ids with a lookup array, not just the list.","Assert class_id.max() < len(classes) in export tests."],"tags":["labelme","export","class-id","validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}