roboflow/supervision · error · ValueError
Empty group detected when non-max-merging detections: {merge
Error message
Empty group detected when non-max-merging detections: {merge_groups} What it means
An internal invariant check inside non-max-merging group construction: after grouping prediction indices by class id and overlap, every group must contain at least one index. An empty group is impossible when inputs are well-formed (groups are built from `np.where(category_ids == id)` results, which are never empty), so hitting this error means the grouping logic received malformed state — effectively a defensive assert against corrupted predictions arrays or patched internals.
Source
Thrown at src/supervision/detection/utils/iou_and_nms.py:1480
When ``predictions`` has no class column, a single pass over all rows is
performed instead of per-category iteration.
"""
if predictions.shape[1] == 5:
global_indices = np.arange(len(predictions), dtype=int)
return [
global_indices[group].tolist() for group in group_within(global_indices)
]
category_ids = predictions[:, 5]
merge_groups: list[list[int]] = []
for category_id in np.unique(category_ids):
curr_indices = np.where(category_ids == category_id)[0]
for local_group in group_within(curr_indices):
merge_groups.append(curr_indices[local_group].tolist())
for merge_group in merge_groups:
if len(merge_group) == 0:
raise ValueError(
f"Empty group detected when non-max-merging detections: {merge_groups}"
)
return merge_groups
def _group_overlapping_boxes(
predictions: npt.NDArray[np.floating],
iou_threshold: float = 0.5,
overlap_metric: OverlapMetric = OverlapMetric.IOU,
) -> list[list[int]]:
"""
Apply greedy version of non-maximum merging to avoid detecting too many
overlapping bounding boxes for a given object.
Args:
predictions: An array of shape `(n, 5)` containing
the bounding boxes coordinates in format `[x1, y1, x2, y2]`
and the confidence scores.View on GitHub (pinned to 7f254d9784)
Solutions
- Use the public API instead: `detections.with_nms(threshold, overlap_filter=sv.OverlapFilter.NON_MAX_MERGE)`.
- If you must call internals, build `predictions` as a float array of shape (N, 6): [x_min, y_min, x_max, y_max, confidence, class_id] with no NaNs.
- Check your test doubles: a stubbed grouping callback that returns empty lists will trip this guard by design.
Example fix
# before groups = _group_overlapping_boxes(np.array([[0, 0, 1, 1, 0.9, np.nan]])) # NaN class id # after dets = detections.with_nms(0.5, overlap_filter=sv.OverlapFilter.NON_MAX_MERGE)
Defensive patterns
Strategy: validation
Validate before calling
assert predictions.ndim == 2 and predictions.shape[1] in (5, 6) assert not np.isnan(predictions.astype(float)).any()
Type guard
def is_valid_predictions(arr) -> bool:
arr = np.asarray(arr)
return arr.ndim == 2 and arr.shape[1] in (5, 6) Try / catch
try:
dets = dets.with_nms(0.5, overlap_filter=sv.OverlapFilter.NON_MAX_MERGE)
except ValueError as e:
if 'Empty group' in str(e):
raise RuntimeError('corrupted detections; rebuild from model output') from e
raise Prevention
- Prefer the public Detections.with_nms API over internal grouping helpers.
- Keep predictions as float arrays with no NaNs in the class-id column.
When it happens
Trigger: Directly calling the private grouping helper `_group_overlapping_boxes` / non-max-merge internals with a hand-built `predictions` array whose shape or dtype breaks the class-id column (e.g. object dtype, NaN class ids making `np.unique`/`np.where` disagree); monkeypatched `group_within` returning empty lists.
Common situations: Almost never seen via the public API (`sv.Detections.with_nms` / `OverlapFilter.NON_MAX_MERGE`); appears when unit tests stub grouping functions or when a custom predictions array with wrong dtype/class column is passed into internal helpers.
Related errors
- `{name}` has shape {arr.shape}; expected (N, 5) or (N, 6).
- Invalid value: {value}. Must be one of {cls.list()}
- KeyPoints detection_confidence must be given for NMS to be e
- KeyPoints class_id must be given for NMS to be executed. If
- Detections class_id must be given for {operation_name} to be
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/94c61ea9edf8397e.
Report an issue: GitHub.