roboflow/supervision · error · ValueError
edges is a dict but class_id is None; KeyPoints must have cl
Error message
edges is a dict but class_id is None; KeyPoints must have class_id set.
What it means
On the main matching path (image has at least one target), Precision pairs predictions with targets by class. Both sides therefore need class_id. The error fires during compute() when either predictions.class_id or targets.class_id is None.
Source
Thrown at src/supervision/key_points/annotators.py:234
... thickness=3,
... edges={0: [(1, 2), (1, 3)], 1: [(1, 2)]},
... )
>>> result = annotator.annotate(image.copy(), key_points)
```
"""
if len(key_points) == 0:
return scene
for detection_index, xy in enumerate(key_points.xy):
if isinstance(self.edges, dict):
class_id = (
int(key_points.class_id[detection_index])
if key_points.class_id is not None
else None
)
if class_id is None:
raise ValueError(
"edges is a dict but class_id is None; "
"KeyPoints must have class_id set."
)
if class_id not in self.edges:
raise ValueError(f"No edges defined for class_id={class_id}.")
edges = self.edges[class_id]
elif self.edges:
edges = self.edges
else:
_looked_up = SKELETONS_BY_VERTEX_COUNT.get(len(xy))
if not _looked_up:
logger.warning("No skeleton found with %d vertices", len(xy))
continue
edges = _looked_up
for edge in edges:
idx_a, idx_b = _validate_edge_indices(edge=edge, vertex_count=len(xy))
xy_a = xy[idx_a]View on GitHub (pinned to 7f254d9784)
Solutions
- Set class_id on both predictions and targets: np.zeros(N, dtype=int) if your task is single-class/class-agnostic
- Re-check any filtering/transformation step (get_by_class_id, slicing) that may have produced class_id-less Detections
- If loading annotations, use sv.Detections.from_coco/from_pascal_voc which preserve class ids, or fix the parser
Example fix
# before
targets = sv.Detections(xyxy=np.array([[30.0, 30.0, 100.0, 100.0]])) # no class_id
precision.update(predictions=[preds], targets=[targets])
precision.compute() # -> ValueError
# after
targets = sv.Detections(
xyxy=np.array([[30.0, 30.0, 100.0, 100.0]]),
class_id=np.array([0]),
)
precision.update(predictions=[preds], targets=[targets]) Defensive patterns
Strategy: validation
Validate before calling
for d in predictions_list + targets_list:
assert d.class_id is not None, 'Precision needs class_id on every Detections' Type guard
def has_class_id(detections: sv.Detections) -> bool:
"""True when Detections carry class ids for class-aware matching."""
return detections.class_id is not None Prevention
- Single-class tasks: default class_id=np.zeros(N, dtype=int) at construction
- Use supervision loaders (from_coco/from_pascal_voc) for ground truth so class ids are preserved
When it happens
Trigger: Calling precision.update() where target Detections were built without class_id (manual construction, or a loader path that dropped class ids) or predictions lack class_id, then compute(). Unlike the background path, prediction confidence is not required here (it is checked separately when predictions are non-empty).
Common situations: Ground-truth built from plain annotation files parsed by hand (boxes + no ids); custom pipelines that merge/transform Detections and lose class_id; evaluating a class-agnostic detector that emits no ids.
Related errors
- No edges defined for class_id={class_id}.
- from_inference() operates on a single result at a time.You c
- Cannot filter keypoints with a 2D boolean mask where rows ha
- MeanAverageRecall metric requires `class_id` on both predict
- F1Score metric requires `class_id` on both predictions and t
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/df37df9f80706464.
Report an issue: GitHub.