roboflow/supervision · error · ValueError
Detection index {detection_idx} is out of bounds for detecti
Error message
Detection index {detection_idx} is out of bounds for detections of length {len(detections)} What it means
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.
Source
Thrown at src/supervision/annotators/utils.py:44
- `TRACK`: Colors are determined by the tracking identifier of the object.
"""
INDEX = "index"
CLASS = "class"
TRACK = "track"
@classmethod
def list(cls) -> list[str]:
return list(map(lambda c: c.value, cls))
def resolve_color_idx(
detections: Detections,
detection_idx: int,
color_lookup: ColorLookup | npt.NDArray[np.int_] = ColorLookup.CLASS,
) -> int:
if detection_idx >= len(detections):
raise ValueError(
f"Detection index {detection_idx} "
f"is out of bounds for detections of length {len(detections)}"
)
if isinstance(color_lookup, np.ndarray):
if len(color_lookup) != len(detections):
raise ValueError(
f"Length of color lookup {len(color_lookup)} "
f"does not match length of detections {len(detections)}"
)
return int(color_lookup[detection_idx])
elif color_lookup == ColorLookup.INDEX:
return detection_idx
elif color_lookup == ColorLookup.CLASS:
if detections.class_id is None:
raise ValueError(
"Could not resolve color by class because "
"Detections do not have class_id. If using an annotator, "View on GitHub (pinned to 7f254d9784)
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.
Example fix
// before detections = detections[keep_mask] # labels still old length annotator.annotate(scene, detections, labels=labels) // after detections = detections[keep_mask] labels = [l for l, k in zip(labels, keep_mask) if k] annotator.annotate(scene, detections, labels=labels)
Defensive patterns
Strategy: validation
Validate before calling
if len(detections) == 0:
return scene # nothing to annotate
if isinstance(color_lookup, np.ndarray):
assert len(color_lookup) == len(detections), (len(color_lookup), len(detections)) Try / catch
try:
annotator.annotate(scene, detections)
except ValueError as e:
if 'out of bounds' in str(e):
raise ValueError('labels/colors desynced from detections after filtering') from e
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- The number of labels ({len(labels)}) does not match the numb
- The provided detections do not contain confidence values. Pl
- Detections must have class_id attribute.
- Detections must include class_id for COCO export.
- class_id is required for CreateML export, but the provided D
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/dc7995130665c7be.
Report an issue: GitHub.