{"record":{"id":"85f8e9751f32376c","repo":"roboflow/supervision","slug":"triggering-anchors-cannot-be-empty-85f8e9","errorCode":null,"errorMessage":"Triggering anchors cannot be empty.","messagePattern":"Triggering anchors cannot be empty\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/detection/tools/polygon_zone.py","lineNumber":85,"sourceCode":"        ... )\n        >>> detections = sv.Detections(xyxy=np.array([[80, 80, 120, 120]]))\n        >>> polygon_zone.trigger(detections)\n        array([ True])\n\n        ```\n    \"\"\"\n\n    def __init__(\n        self,\n        polygon: npt.NDArray[np.int64],\n        triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),\n        require_all_anchors: bool = True,\n    ) -> None:\n        self.polygon = polygon.astype(int)\n        # Materialize once so we can safely accept generators without exhausting them.\n        self.triggering_anchors = list(triggering_anchors)\n        if not self.triggering_anchors:\n            raise ValueError(\"Triggering anchors cannot be empty.\")\n        self.require_all_anchors = require_all_anchors\n\n        self.current_count = 0\n\n        x_max, y_max = np.max(polygon, axis=0)\n        self.mask = polygon_to_mask(\n            polygon=polygon, resolution_wh=(x_max + 2, y_max + 2)\n        )\n\n    def trigger(self, detections: Detections) -> npt.NDArray[np.bool_]:\n        \"\"\"\n        Determines if the detections are within the polygon zone.\n\n        Anchor points are calculated from original (unclipped) detection boxes to\n        avoid per-zone clipping shifting anchor positions. This prevents a single\n        detection from being counted in multiple non-overlapping zones due to\n        clipping artifacts, although overlapping zones may still legitimately\n        contain the same detection.","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/detection/tools/polygon_zone.py#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass at least one anchor, the typical default being triggering_anchors=[Position.BOTTOM_CENTER].","Validate the config source before constructing the zone: raise a clear config error when the anchors field is empty.","If the emptiness is legitimate in your flow (zone disabled), skip constructing the PolygonZone entirely."],"exampleFix":"# before\nzone = sv.PolygonZone(polygon=polygon, triggering_anchors=[])\n\n# after\nzone = sv.PolygonZone(polygon=polygon, triggering_anchors=[sv.Position.BOTTOM_CENTER])","handlingStrategy":"validation","validationCode":"anchors = list(triggering_anchors) or [sv.Position.BOTTOM_CENTER]\nif not anchors:\n    raise ValueError('config error: triggering_anchors is empty')\nzone = sv.PolygonZone(polygon=polygon, triggering_anchors=anchors)","typeGuard":"def has_triggering_anchor(items) -> bool:\n    return bool(list(items))","tryCatchPattern":null,"preventionTips":["Validate zone config (polygon and anchors) at load time, before the processing loop starts.","Default empty config to [Position.BOTTOM_CENTER] rather than [] in your own config layer."],"tags":["polygon-zone","anchors","validation","valueerror"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}