{"record":{"id":"dc7995130665c7be","repo":"roboflow/supervision","slug":"detection-index-detection-idx-is-out-of-bounds-f","errorCode":null,"errorMessage":"Detection index {detection_idx} is out of bounds for detections of length {len(detections)}","messagePattern":"Detection index (.+?) is out of bounds for detections of length (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/annotators/utils.py","lineNumber":44,"sourceCode":"        - `TRACK`: Colors are determined by the tracking identifier of the object.\n    \"\"\"\n\n    INDEX = \"index\"\n    CLASS = \"class\"\n    TRACK = \"track\"\n\n    @classmethod\n    def list(cls) -> list[str]:\n        return list(map(lambda c: c.value, cls))\n\n\ndef resolve_color_idx(\n    detections: Detections,\n    detection_idx: int,\n    color_lookup: ColorLookup | npt.NDArray[np.int_] = ColorLookup.CLASS,\n) -> int:\n    if detection_idx >= len(detections):\n        raise ValueError(\n            f\"Detection index {detection_idx} \"\n            f\"is out of bounds for detections of length {len(detections)}\"\n        )\n\n    if isinstance(color_lookup, np.ndarray):\n        if len(color_lookup) != len(detections):\n            raise ValueError(\n                f\"Length of color lookup {len(color_lookup)} \"\n                f\"does not match length of detections {len(detections)}\"\n            )\n        return int(color_lookup[detection_idx])\n    elif color_lookup == ColorLookup.INDEX:\n        return detection_idx\n    elif color_lookup == ColorLookup.CLASS:\n        if detections.class_id is None:\n            raise ValueError(\n                \"Could not resolve color by class because \"\n                \"Detections do not have class_id. If using an annotator, \"","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/annotators/utils.py#L26-L62","documentation":"Raised by resolve_color_idx() in supervision.annotators.utils when detection_idx is >= len(detections) while resolving a color for one detection. Annotators iterate detections and index into them per box; an index past the end means the detections object and the per-detection metadata (labels, custom colors) have diverged, which the helper catches before it would raise a confusing NumPy IndexError.","triggerScenarios":"Annotating with a custom color_lookup array longer than detections; filtering detections between building labels and calling annotate(); passing detections.empty() to an annotator with a stale nonzero index; manual loops that enumerate a different list than the detections passed in.","commonSituations":"Code that slices detections (e.g. detections[np.array([0,2])]) but keeps the old labels list; race conditions in streaming pipelines where detections are replaced between construction and annotation; off-by-one in custom annotator subclasses.","solutions":["Recompute labels/colors after every filter: labels = [labels[i] for i in kept_indices].","Ensure len(color_lookup_array) == len(detections) when passing an ndarray.","Guard empty detections: if len(detections) == 0: skip annotate().","In custom loops, iterate enumerate(detections) rather than a separate cached list."],"exampleFix":"// before\ndetections = detections[keep_mask]  # labels still old length\nannotator.annotate(scene, detections, labels=labels)\n\n// after\ndetections = detections[keep_mask]\nlabels = [l for l, k in zip(labels, keep_mask) if k]\nannotator.annotate(scene, detections, labels=labels)","handlingStrategy":"validation","validationCode":"if len(detections) == 0:\n    return scene  # nothing to annotate\nif isinstance(color_lookup, np.ndarray):\n    assert len(color_lookup) == len(detections), (len(color_lookup), len(detections))","typeGuard":null,"tryCatchPattern":"try:\n    annotator.annotate(scene, detections)\nexcept ValueError as e:\n    if 'out of bounds' in str(e):\n        raise ValueError('labels/colors desynced from detections after filtering') from e\n    raise","preventionTips":["Rebuild all per-detection metadata (labels, color arrays) right after any filter/slice.","Early-return on empty detections before annotating.","Apply the same boolean mask to detections and every parallel list in one place."],"tags":["annotators","bounds-check","detections","filtering"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}