roboflow/supervision · error · ValueError

CreateML annotation file contains duplicate entries for imag

Error message

CreateML annotation file contains duplicate entries for image {image_name}. Each image must appear at most once.

What it means

Raised by load_createml_annotations when two entries resolve to the same canonical image path (dict lookup on the resolved path). Each image may appear at most once; a duplicate would silently overwrite the earlier entry's Detections.

Source

Thrown at src/supervision/dataset/formats/createml.py:211

    image_paths: list[str] = []
    annotations: dict[str, Detections] = {}
    for entry in tqdm(
        createml_data,
        desc="Loading CreateML annotations",
        disable=not show_progress,
    ):
        image_name = entry.get("image")
        if image_name is None:
            raise ValueError(
                f"CreateML annotation entry is missing the required 'image' key: "
                f"{entry!r}"
            )
        image_path = _resolve_image_path(
            images_directory_path=images_directory_path, image_name=image_name
        )
        if image_path in annotations:
            raise ValueError(
                f"CreateML annotation file contains duplicate entries for image "
                f"{image_name!r}. Each image must appear at most once."
            )
        annotations[image_path] = createml_annotations_to_detections(
            image_annotations=entry.get("annotations") or [],
            class_to_index=class_to_index,
        )
        image_paths.append(image_path)

    return classes, image_paths, annotations


def detections_to_createml_annotations(
    detections: Detections, classes: list[str]
) -> list[CreateMLDict]:
    """Convert ``Detections`` into a list of CreateML annotation dicts.

    Each bounding box is stored as a pixel-space centre point plus width and

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Deduplicate entries on the resolved image path, merging their annotations arrays if both are valid.
  2. Check for duplicates before loading: paths = [Path(imgs_dir, e['image']).resolve() for e in data]; assert len(set(paths)) == len(paths).
  3. If two distinct images share a basename, put them in subfolders and use distinct relative paths.

Example fix

// before
[{"image": "a.jpg", "annotations": [...]}, {"image": "./a.jpg", "annotations": [...]}]

// after
[{"image": "a.jpg", "annotations": [...merged...]}]
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

def assert_unique_createml_images(annotations_path: str, images_dir: str) -> None:
    """Fail fast if two entries resolve to the same image path."""
    entries = json.loads(Path(annotations_path).read_text())
    seen: set[str] = set()
    for e in entries:
        p = str(Path(images_dir, e["image"]).resolve())
        if p in seen:
            raise ValueError(f"Duplicate image {e['image']!r}")
        seen.add(p)

Type guard

def createml_images_are_unique(entries: list[dict], images_dir: str) -> bool:
    """True when all resolved image paths are distinct."""
    paths = [str(Path(images_dir, e["image"]).resolve()) for e in entries]
    return len(set(paths)) == len(paths)

Try / catch

try:
    sv.DetectionDataset.from_createml(images_directory_path=d, annotations_path=a)
except ValueError as exc:
    if "duplicate entries" in str(exc):
        # merge annotations arrays for duplicated images, then retry
        ...
    raise

Prevention

When it happens

Trigger: Two entries with the same "image" value, or aliasing values ("a.jpg" vs "./a.jpg") that collapse after resolution — the resolver deliberately canonicalizes so aliases do not sneak past this check.

Common situations: Concatenating per-batch CreateML files without deduplication; copy-pasted entries; case-insensitive filesystems where differently-cased names collide.

Related errors


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