{"record":{"id":"9cbbba352417f618","repo":"roboflow/supervision","slug":"edge-indices-must-use-the-1-based-convention-and-b","errorCode":null,"errorMessage":"Edge indices must use the 1-based convention and be within the available keypoint range [1, {vertex_count}], got {edge}.","messagePattern":"Edge indices must use the 1-based convention and be within the available keypoint range \\[1, (.+?)\\], got (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/key_points/annotators.py","lineNumber":26,"sourceCode":"from supervision import _cv2 as cv2\nfrom supervision.detection.utils.boxes import pad_boxes, spread_out_boxes\nfrom supervision.draw.base import ImageType\nfrom supervision.draw.color import Color\nfrom supervision.draw.utils import draw_rounded_rectangle\nfrom supervision.geometry.core import Rect\nfrom supervision.key_points.core import KeyPoints\nfrom supervision.key_points.skeletons import SKELETONS_BY_VERTEX_COUNT\nfrom supervision.utils.conversion import ensure_cv2_image_for_class_method\nfrom supervision.utils.logger import _get_logger\n\nlogger = _get_logger(__name__)\n\n\ndef _validate_edge_indices(edge: tuple[int, int], vertex_count: int) -> tuple[int, int]:\n    \"\"\"Validate 1-based skeleton edges and return zero-based vertex indexes.\"\"\"\n    vertex_a, vertex_b = edge\n    if not (1 <= vertex_a <= vertex_count and 1 <= vertex_b <= vertex_count):\n        raise ValueError(\n            \"Edge indices must use the 1-based convention and be within the \"\n            f\"available keypoint range [1, {vertex_count}], got {edge}.\"\n        )\n    # Public skeleton definitions are 1-based; keypoint arrays are zero-based.\n    return vertex_a - 1, vertex_b - 1\n\n\nclass BaseKeyPointAnnotator(ABC):\n    @abstractmethod\n    def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:\n        pass\n\n\nclass VertexAnnotator(BaseKeyPointAnnotator):\n    \"\"\"\n    A class that specializes in drawing skeleton vertices on images. It uses\n    specified key points to determine the locations where the vertices should be\n    drawn.","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/key_points/annotators.py#L8-L44","documentation":"Raised in Color.__post_init__ when a Color dataclass is constructed directly with any channel (r, g, b, or a) outside the 0-255 byte range. supervision validates eagerly at construction time because all downstream consumers (OpenCV drawing, image compositing) require byte-sized channel values. The message includes the offending (r, g, b, a) tuple so the out-of-range channel is immediately visible.","triggerScenarios":"Direct construction such as sv.Color(r=300, g=0, b=0), sv.Color(-1, 0, 0), or sv.Color(256, 256, 256, 300); arithmetic on colors without clamping, e.g. brightening via sv.Color(c.r + 50, c.g + 50, c.b + 50); passing floats or channel values from a library that uses 0-1 or 0-65535 ranges.","commonSituations":"Dynamic color math (tints, gradients, heatmaps) that overflows the byte range; migrating code from libraries with different color ranges (PIL RGB floats, CSS percentages, 16-bit image pipelines); reading channel values from external config or data files without bounds checking.","solutions":["Clamp every channel to 0-255 before constructing, e.g. sv.Color(max(0, min(255, r)), ...).","If your source values are 0-1 floats, convert with int(round(v * 255)) first.","Prefer the classmethods from_rgb_tuple / from_bgr_tuple / from_rgba_tuple / from_bgra_tuple or from_hex, which validate per format and document expected ranges.","Audit any code that adds/multiplies channel values (fade-in effects, mixing) and add a clamp helper at the boundary."],"exampleFix":"# before\nbrightened = sv.Color(base.r + 60, base.g + 60, base.b + 60)  # may exceed 255\n\n# after\nclamp = lambda v: max(0, min(255, v))\nbrightened = sv.Color(clamp(base.r + 60), clamp(base.g + 60), clamp(base.b + 60))","handlingStrategy":"validation","validationCode":"def clamp_channel(v: float) -> int:\n    \"\"\"Force any channel value into the 0-255 byte range Color requires.\"\"\"\n    return max(0, min(255, int(round(v))))\n\n# before: sv.Color(r + 40, g + 40, b + 40)\n# after:  sv.Color(clamp_channel(r + 40), clamp_channel(g + 40), clamp_channel(b + 40))","typeGuard":"def is_byte_color(r: float, g: float, b: float, a: float = 255) -> bool:\n    \"\"\"True if all channels are ints within 0-255, safe for sv.Color().\"\"\"\n    return all(isinstance(v, int) and 0 <= v <= 255 for v in (r, g, b, a))","tryCatchPattern":null,"preventionTips":["Route all dynamic color creation through a single clamping helper instead of constructing sv.Color inline.","Never feed 0-1 float or 16-bit channel values directly; convert ranges explicitly at the boundary.","Use the from_*_tuple classmethods so format-specific validation and clearer messages apply."],"tags":["validation","color","range","valueerror"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}