roboflow/supervision · error · ValueError

The number of labels ({len(labels)}) does not match the numb

Error message

The number of labels ({len(labels)}) does not match the number of detections ({len(detections)}). Each detection should have exactly 1 label.

What it means

Raised by _validate_labels() in supervision.annotators.utils when a non-None labels list passed to an annotator has a different length than the Detections object being annotated. Each detection must map to exactly one label; a mismatch means labels and boxes are misaligned, which would otherwise produce wrong or IndexError-prone rendering.

Source

Thrown at src/supervision/annotators/utils.py:229

    return all_lines or [""]


def _validate_labels(labels: list[str] | None, detections: Detections) -> None:
    """
    Validates that the number of provided labels matches the number of detections.

    Args:
        labels: A list of labels, one for each detection. Can
            be None.
        detections: The detections to be labeled.

    Raises:
        ValueError: If `labels` is not None and its length does not match the number
            of detections.
    """
    if labels is not None and len(labels) != len(detections):
        raise ValueError(
            f"The number of labels ({len(labels)}) does not match the "
            f"number of detections ({len(detections)}). Each detection "
            f"should have exactly 1 label."
        )


@deprecated(  # type: ignore[untyped-decorator]
    target=_validate_labels,
    deprecated_in="0.29.0",
    remove_in="0.32.0",
)
def validate_labels(labels: list[str] | None, detections: Detections) -> None:
    void(labels, detections)


def get_labels_text(
    detections: Detections, custom_labels: list[str] | None
) -> list[str]:

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Rebuild labels from the same detections you annotate: labels = [f'{names[c]} {conf:.2f}' for c, conf in zip(detections.class_id, detections.confidence)].
  2. After any mask/slice of detections, apply the same mask to labels.
  3. Pass labels=None when you do not want labels, instead of an empty list for nonempty detections.
  4. Add an assert len(labels) == len(detections) right before annotate() while debugging.

Example fix

// before
mask = detections.confidence > 0.5
detections = detections[mask]
annotator.annotate(scene, detections, labels=labels)  # stale labels

// after
mask = detections.confidence > 0.5
detections = detections[mask]
labels = [l for l, keep in zip(labels, mask) if keep]
annotator.annotate(scene, detections, labels=labels)
Defensive patterns

Strategy: validation

Validate before calling

assert len(detections) == len(labels), (len(detections), len(labels))
annotator.annotate(scene, detections, labels=labels)

# or derive labels from the same detections object:
labels = [str(c) for c in detections.class_id] if detections.class_id is not None else None

Try / catch

try:
    annotator.annotate(scene, detections, labels=labels)
except ValueError as e:
    if 'number of labels' in str(e):
        labels = labels[: len(detections)]
        annotator.annotate(scene, detections, labels=labels)
    else:
        raise

Prevention

When it happens

Trigger: Calling annotator.annotate(scene, detections, labels=labels) after filtering detections but not labels; building labels from class names of a previous frame in a tracker loop; passing a single string instead of a per-detection list; labeling detections.empty() with a nonempty list.

Common situations: Post-filtering with a confidence or class mask; tracked pipelines where detections change but a static label list is reused; multi-threaded consumers mutating detections between label generation and annotation; forgetting to wrap a single label in a list.

Related errors


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