{"record":{"id":"1aacd3c88fda935c","repo":"roboflow/supervision","slug":"invalid-hex-digits-in-hex-color","errorCode":null,"errorMessage":"Invalid hex digits in {hex_color}","messagePattern":"Invalid hex digits in (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/annotators/utils.py","lineNumber":459,"sourceCode":"        >>> hex_to_rgba(\"#FF00FF\")\n        (255, 0, 255, 255)\n        >>> hex_to_rgba(\"#FF00FF80\")\n        (255, 0, 255, 128)\n\n        ```\n    \"\"\"\n    hex_color = hex_color.strip().removeprefix(\"#\")\n    if len(hex_color) == 6:\n        hex_color += \"FF\"  # default full opacity\n    if len(hex_color) != 8:\n        raise ValueError(f\"Invalid hex color format: {hex_color}\")\n    try:\n        r = int(hex_color[0:2], 16)\n        g = int(hex_color[2:4], 16)\n        b = int(hex_color[4:6], 16)\n        a = int(hex_color[6:8], 16)\n    except ValueError as exc:\n        raise ValueError(f\"Invalid hex digits in {hex_color}\") from exc\n    return (r, g, b, a)\n\n\ndef rgba_to_hex(rgba: tuple[int, int, int, int]) -> str:\n    \"\"\"\n    Converts an RGBA tuple (0-255 each) to a hex color string.\n\n    Args:\n        rgba: RGBA values in range 0-255.\n\n    Returns:\n        Hex color string in the format \"#RRGGBBAA\".\n\n    Raises:\n        ValueError: If `rgba` is not a 4-tuple or contains values outside 0-255.\n\n    Examples:\n        ```pycon","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/annotators/utils.py#L441-L477","documentation":"Raised by `hex_to_rgba` when the string has the correct length (6 or 8 after '#' removal) but contains characters that are not valid hexadecimal digits. The length check passes first, then `int(slice, 16)` fails and the error is re-raised with a clearer message.","triggerScenarios":"Calling `hex_to_rgba(\"#GG00FF\")` (G is not hex), `hex_to_rgba(\"FF00FF8O\")` (letter O instead of zero), or strings containing whitespace in the middle or punctuation like '#FF,0FF'. Mixed-case valid hex ('#ff00ff') does NOT trigger this — only non-hex characters do.","commonSituations":"OCR or hand-transcribed color values with O/0 or l/1 confusion; color strings loaded from CSV/JSON config containing typos; locale-related full-width characters pasted from rich-text editors; colors copied from design tools that emit shorthand like '#FFF' hit error 280 instead, while character typos hit this one.","solutions":["Fix the offending character — every character must be 0-9, a-f, or A-F.","Validate user-supplied colors at load time with `is_valid_hex` from `supervision.annotators.utils` and reject early with a clear config error.","If colors come from an untrusted source, normalize/scrub non-ASCII characters before parsing."],"exampleFix":"# before\nsv.Color.from_hex(\"#FF00FF8O\")  # letter O -> ValueError: Invalid hex digits\n\n# after\nsv.Color.from_hex(\"#FF00FF80\")  # zero","handlingStrategy":"validation","validationCode":"from supervision.annotators.utils import is_valid_hex\n\nfor name, color in config['colors'].items():\n    if not is_valid_hex(color):\n        raise ConfigError(f\"invalid hex color for {name}: {color!r}\")","typeGuard":"import re\nHEX_RE = re.compile(r'^#?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$')\n\ndef is_hex_color(s: str) -> bool:\n    return isinstance(s, str) and HEX_RE.match(s) is not None","tryCatchPattern":null,"preventionTips":["Validate colors from user input/config once at startup with a regex or is_valid_hex.","Watch for O-vs-0 and l-vs-1 in hand-transcribed hex strings."],"tags":["color","validation","annotators"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}