roboflow/supervision · error · ValueError
The keys of the images and annotations dictionaries must mat
Error message
The keys of the images and annotations dictionaries must match.
What it means
Raised by the DetectionDataset constructor when the set of image keys does not equal the set of annotation keys. Every image must have exactly one Detections annotation and vice versa; a mismatch means the dataset would have images without labels or labels pointing at nonexistent images.
Source
Thrown at src/supervision/dataset/core.py:101
be removed in ``0.33.0``; use a list of paths instead.
When a list of paths is provided, images are loaded lazily on
demand, which is more memory-efficient.
annotations: Dictionary mapping
image path to annotations. The dictionary keys match
match the keys in `images` or entries in the list of
image paths.
"""
def __init__(
self,
classes: list[str],
images: list[str] | dict[str, npt.NDArray[np.uint8]],
annotations: dict[str, Detections],
) -> None:
self.classes = classes
if set(images) != set(annotations):
raise ValueError(
"The keys of the images and annotations dictionaries must match."
)
self.annotations = {
image_path: deepcopy(annotation)
for image_path, annotation in annotations.items()
}
np_classes = np.array(self.classes)
for image_path, annotation in self.annotations.items():
class_ids = annotation.class_id
if class_ids is None:
continue
if not np.issubdtype(class_ids.dtype, np.integer):
raise ValueError(
f"Detection annotation for image {image_path!r} contains "
f"non-integer class_id values with dtype {class_ids.dtype}."
)
View on GitHub (pinned to 7f254d9784)
Solutions
- Intersect the two key sets before constructing: keep = set(images) & set(annotations), then filter both.
- Normalize paths (same suffix, same prefix, os.path.normpath) so keys compare equal.
- Log the symmetric difference set(images) ^ set(annotations) to see exactly which keys mismatch.
Example fix
// before
images = {p: cv2.imread(p) for p in glob('images/*')}
ds = DetectionDataset(classes=c, images=images, annotations=anns) # key mismatch
// after
keep = sorted(set(images) & set(anns))
ds = DetectionDataset(classes=c, images=[p for p in keep], annotations={p: anns[p] for p in keep}) Defensive patterns
Strategy: validation
Validate before calling
if set(images) != set(annotations):
keep = set(images) & set(annotations)
missing = set(images) ^ set(annotations)
print(f"Skipping unmatched entries: {missing}")
images = {p: images[p] for p in images if p in keep} if isinstance(images, dict) else [p for p in images if p in keep]
annotations = {p: annotations[p] for p in annotations if p in keep}
ds = DetectionDataset(classes=classes, images=images, annotations=annotations) Type guard
def keys_match(images, annotations: dict) -> bool:
return set(images) == set(annotations) Prevention
- Derive annotations and images from one source of truth (one glob loop).
- Normalize paths (abspath, same extension casing) before building the dicts.
- Log the symmetric difference of key sets during dataset construction debugging.
When it happens
Trigger: Calling DetectionDataset(classes=..., images={'a.jpg': img}, annotations={'b.jpg': dets}) — key 'a.jpg' vs 'b.jpg'. Also when passing images as a list while the annotations dict keys don't exactly match the list entries (set comparison still applies).
Common situations: Building a dataset from globbed image files and a parsed annotations dict where some images failed to parse or some annotation files have no matching image; off-by-one filename differences ('.jpeg' vs '.jpg'); leading './' in one set but not the other.
Related errors
- Detections must have class_id attribute.
- Detections class_id must be a subset of source_to_target_map
- Class {class_name} not found in target classes. source_class
- Cannot export dataset: image paths {first_path} and {image_p
- Image paths {duplicates} are not unique across datasets.
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/50ebc8edffe81beb.
Report an issue: GitHub.