roboflow/supervision · error · ValueError

The number of line zones and their labels must match.

Error message

The number of line zones and their labels must match.

What it means

LineZoneAnnotator.annotate(frame, line_zones, line_zone_labels) requires one label per line zone; when explicit labels are provided their count must equal len(line_zones). Labels are zipped with zones to render per-zone crossing counts, so a mismatch means ambiguous output and is rejected.

Source

Thrown at src/supervision/detection/line_zone.py:831

        line_zone_labels: list[str] | None = None,
    ) -> npt.NDArray[np.uint8]:
        """
        Draws a table with the number of objects of each class that crossed each line.

        Args:
            frame: The image on which the table will be drawn.
            line_zones: The line zones to be annotated.
            line_zone_labels: The labels, one for each line zone. If not
                provided, the default labels will be used.

        Returns:
            The image with the table drawn on it.

        """
        if line_zone_labels is None:
            line_zone_labels = [f"Line {i + 1}:" for i in range(len(line_zones))]
        if len(line_zones) != len(line_zone_labels):
            raise ValueError("The number of line zones and their labels must match.")

        text_lines = ["Line Crossings:"]
        for line_zone, line_zone_label in zip(line_zones, line_zone_labels):
            text_lines.append(line_zone_label)
            class_id_to_name = line_zone.class_id_to_name

            for direction, count_per_class in [
                ("In", line_zone.in_count_per_class),
                ("Out", line_zone.out_count_per_class),
            ]:
                if not count_per_class:
                    continue

                text_lines.append(f" {direction}:")
                for class_id, count in count_per_class.items():
                    if self.force_draw_class_ids:
                        class_name = str(class_id)
                    elif class_id is None:

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Build labels from the zones themselves: line_zone_labels=[f'Zone {i+1}' for i in range(len(zones))] or omit them to get defaults.
  2. Keep zones and labels in the same config block so they cannot diverge.
  3. Add an assert len(line_zones) == len(labels) near where either is defined.

Example fix

# before
 annotator.annotate(
     frame=frame,
     line_zones=zones,               # 3 zones
     line_zone_labels=["In", "Out"],  # 2 labels
 )

# after
 annotator.annotate(
     frame=frame,
     line_zones=zones,
     line_zone_labels=[f"Zone {i + 1}" for i in range(len(zones))],
 )
Defensive patterns

Strategy: validation

Validate before calling

if line_zone_labels is not None:
    assert len(line_zones) == len(line_zone_labels), (
        f"{len(line_zones)} zones vs {len(line_zone_labels)} labels"
    )
annotator.annotate(frame=frame, line_zones=line_zones, line_zone_labels=line_zone_labels)

Prevention

When it happens

Trigger: Passing 3 zones with 2 labels (e.g. reusing a hardcoded label list after adding a zone), or a label list generated from a different config section than the zones (['Entrance', 'Exit'] vs 4 configured zones).

Common situations: Zones defined in a config file and labels hardcoded in code, drifting out of sync; iterating a subset of zones but not the labels; adding a new zone during a demo without updating labels.

Related errors


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