roboflow/supervision · error · ValueError

Triggering anchors cannot be empty.

Error message

Triggering anchors cannot be empty.

What it means

Raised by PolygonZone.__init__ when triggering_anchors materializes to an empty list. The zone decides in/out membership purely by testing the configured anchor points of each box; with zero anchors there is no predicate to evaluate, so construction fails fast rather than silently matching nothing.

Source

Thrown at src/supervision/detection/tools/polygon_zone.py:85

        ... )
        >>> detections = sv.Detections(xyxy=np.array([[80, 80, 120, 120]]))
        >>> polygon_zone.trigger(detections)
        array([ True])

        ```
    """

    def __init__(
        self,
        polygon: npt.NDArray[np.int64],
        triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),
        require_all_anchors: bool = True,
    ) -> None:
        self.polygon = polygon.astype(int)
        # Materialize once so we can safely accept generators without exhausting them.
        self.triggering_anchors = list(triggering_anchors)
        if not self.triggering_anchors:
            raise ValueError("Triggering anchors cannot be empty.")
        self.require_all_anchors = require_all_anchors

        self.current_count = 0

        x_max, y_max = np.max(polygon, axis=0)
        self.mask = polygon_to_mask(
            polygon=polygon, resolution_wh=(x_max + 2, y_max + 2)
        )

    def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]:
        """
        Determines if the detections are within the polygon zone.

        Anchor points are calculated from original (unclipped) detection boxes to
        avoid per-zone clipping shifting anchor positions. This prevents a single
        detection from being counted in multiple non-overlapping zones due to
        clipping artifacts, although overlapping zones may still legitimately
        contain the same detection.

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Pass at least one anchor, the typical default being triggering_anchors=[Position.BOTTOM_CENTER].
  2. Validate the config source before constructing the zone: raise a clear config error when the anchors field is empty.
  3. If the emptiness is legitimate in your flow (zone disabled), skip constructing the PolygonZone entirely.

Example fix

# before
zone = sv.PolygonZone(polygon=polygon, triggering_anchors=[])

# after
zone = sv.PolygonZone(polygon=polygon, triggering_anchors=[sv.Position.BOTTOM_CENTER])
Defensive patterns

Strategy: validation

Validate before calling

anchors = list(triggering_anchors) or [sv.Position.BOTTOM_CENTER]
if not anchors:
    raise ValueError('config error: triggering_anchors is empty')
zone = sv.PolygonZone(polygon=polygon, triggering_anchors=anchors)

Type guard

def has_triggering_anchor(items) -> bool:
    return bool(list(items))

Prevention

When it happens

Trigger: Passing triggering_anchors=[] to PolygonZone; passing a generator that is already exhausted; building the anchor list programmatically (e.g. from CLI flags or a config filter) that ends up empty.

Common situations: Config-driven applications where an anchor list comes from a YAML/JSON file and the user leaves it empty or filters out all entries; refactoring that accidentally passes an empty tuple default.

Related errors


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