roboflow/supervision · error · ValueError

KeyPoints class_id must be given for NMS to be executed. If

Error message

KeyPoints class_id must be given for NMS to be executed. If you intended to perform class agnostic NMS set class_agnostic=True.

What it means

Raised by KeyPoints.with_nms() when class_id is None and class_agnostic is False. Standard NMS suppresses overlaps only within the same class, which requires class_id; supervision makes this requirement explicit and tells you the escape hatch: set class_agnostic=True to suppress across all classes without class_id.

Source

Thrown at src/supervision/key_points/core.py:1355

            from rfdetr import RFDETRKeypointPreview

            image = cv2.imread("<SOURCE_IMAGE_PATH>")
            model = RFDETRKeypointPreview()

            key_points = model.predict(image)
            key_points = key_points.with_nms(threshold=0.5)
            ```
        """
        if len(self) == 0:
            return self

        if self.detection_confidence is None:
            raise ValueError(
                "KeyPoints detection_confidence must be given for NMS to be executed."
            )

        if not class_agnostic and self.class_id is None:
            raise ValueError(
                "KeyPoints class_id must be given for NMS to be executed. If "
                "you intended to perform class agnostic NMS set "
                "class_agnostic=True."
            )

        xy = self.xy
        valid = ~np.all(xy == 0, axis=-1)
        if self.visible is not None:
            valid = valid & self.visible
        x_min = np.min(np.where(valid, xy[..., 0], np.inf), axis=1)
        y_min = np.min(np.where(valid, xy[..., 1], np.inf), axis=1)
        x_max = np.max(np.where(valid, xy[..., 0], -np.inf), axis=1)
        y_max = np.max(np.where(valid, xy[..., 1], -np.inf), axis=1)
        xyxy = np.stack([x_min, y_min, x_max, y_max], axis=1).astype(np.float32)

        if class_agnostic:
            predictions = np.hstack([xyxy, self.detection_confidence.reshape(-1, 1)])
        else:

View on GitHub (pinned to 7f254d9784)

Solutions

  1. If detections are all one class or class boundaries do not matter: key_points.with_nms(threshold=0.5, class_agnostic=True).
  2. Otherwise pass class_id when constructing KeyPoints so class-aware NMS can group by class.

Example fix

// before
kp = kp.with_nms(threshold=0.5)  # no class_id -> ValueError

// after
kp = kp.with_nms(threshold=0.5, class_agnostic=True)
Defensive patterns

Strategy: validation

Validate before calling

if kp.class_id is None:
    kp = kp.with_nms(threshold=0.5, class_agnostic=True)
else:
    kp = kp.with_nms(threshold=0.5)

Type guard

def nms_kwargs(kp: sv.KeyPoints) -> dict:
    return {"class_agnostic": kp.class_id is None}

Prevention

When it happens

Trigger: Calling key_points.with_nms(threshold=0.5) (class_agnostic defaults to False) on KeyPoints that lack class_id — e.g. a single-person pose result or a connector that does not emit class ids.

Common situations: Running NMS on pose/keypoint output from a single-class model that never populated class_id; forgetting that with_nms defaults to class-aware NMS unlike the intended use case.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/922722c9bcb88038. Report an issue: GitHub.