roboflow/supervision · error · ValueError

Malformed CreateML annotation entry (missing or non-string '

Error message

Malformed CreateML annotation entry (missing or non-string 'label'): {exc}

What it means

Raised by load_createml_annotations when collecting the class set across all entries fails with KeyError/TypeError — i.e. some annotation lacks a 'label' key or has a non-subscriptable shape. The loader derives the sorted class list from every annotation's label before building class_to_index, so one malformed annotation aborts the load.

Source

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

    createml_data = cast(
        "list[CreateMLDict]", read_json_file(file_path=annotations_path)
    )
    if not isinstance(createml_data, list):
        raise ValueError(
            f"CreateML annotation file must contain a JSON list at the root, "
            f"got {type(createml_data).__name__}."
        )

    try:
        classes = sorted(
            {
                annotation["label"]
                for entry in createml_data
                for annotation in (entry.get("annotations") or [])
            }
        )
    except (KeyError, TypeError) as exc:
        raise ValueError(
            f"Malformed CreateML annotation entry "
            f"(missing or non-string 'label'): {exc}"
        ) from exc
    class_to_index = {class_name: index for index, class_name in enumerate(classes)}

    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}"
            )

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Scan the JSON for annotations without a string 'label' field and add or rename it.
  2. If your source uses a different key, transform it: ann['label'] = ann.pop('category').
  3. Drop null/non-dict items from annotations arrays before loading.

Example fix

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

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

Strategy: validation

Validate before calling

import json
from pathlib import Path

def validate_createml_labels(annotations_path: str) -> None:
    """Fail fast if any annotation lacks a string 'label'."""
    entries = json.loads(Path(annotations_path).read_text())
    for e in entries:
        for ann in e.get("annotations") or []:
            if not isinstance(ann, dict) or not isinstance(ann.get("label"), str):
                raise ValueError(f"Malformed annotation in entry {e.get('image')!r}: {ann!r}")

Type guard

def has_valid_labels(entry: dict) -> bool:
    """True when every annotation in the entry carries a string label."""
    anns = entry.get("annotations") or []
    return all(isinstance(a, dict) and isinstance(a.get("label"), str) for a in anns)

Try / catch

try:
    sv.DetectionDataset.from_createml(images_directory_path=d, annotations_path=a)
except ValueError as exc:
    if "missing or non-string 'label'" in str(exc):
        validate_createml_labels(a)  # raises with the precise offending entry
    raise

Prevention

When it happens

Trigger: Any entry in the annotations array of the CreateML JSON missing "label", or an annotations list containing non-dict items (TypeError on annotation["label"]).

Common situations: Partially exported or hand-edited files; annotations written by a tool that names the field differently (e.g. 'class' or 'category'); null entries inside annotations arrays.

Understand the failure class

Related errors


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