{"record":{"id":"018c4669087bbe74","repo":"Textualize/rich","slug":"color-components-must-be-255-in-original-color","errorCode":null,"errorMessage":"color components must be <= 255 in {original_color!r}","messagePattern":"color components must be <= 255 in (.+?)","errorType":"validation","errorClass":"ColorParseError","httpStatus":null,"severity":"error","filePath":"rich/color.py","lineNumber":479,"sourceCode":"            number = int(color_8)\n            if number > 255:\n                raise ColorParseError(f\"color number must be <= 255 in {color!r}\")\n            return cls(\n                color,\n                type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT),\n                number=number,\n            )\n\n        else:  #  color_rgb:\n            components = color_rgb.split(\",\")\n            if len(components) != 3:\n                raise ColorParseError(\n                    f\"expected three components in {original_color!r}\"\n                )\n            red, green, blue = components\n            triplet = ColorTriplet(int(red), int(green), int(blue))\n            if not all(component <= 255 for component in triplet):\n                raise ColorParseError(\n                    f\"color components must be <= 255 in {original_color!r}\"\n                )\n            return cls(color, ColorType.TRUECOLOR, triplet=triplet)\n\n    @lru_cache(maxsize=1024)\n    def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]:\n        \"\"\"Get the ANSI escape codes for this color.\"\"\"\n        _type = self.type\n        if _type == ColorType.DEFAULT:\n            return (\"39\" if foreground else \"49\",)\n\n        elif _type == ColorType.WINDOWS:\n            number = self.number\n            assert number is not None\n            fore, back = (30, 40) if number < 8 else (82, 92)\n            return (str(fore + number if foreground else back + number),)\n\n        elif _type == ColorType.STANDARD:","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/Textualize/rich/blob/9d8f9a372cc5916fd4781fec207ced7ddac2f08f/rich/color.py#L461-L497","documentation":"After splitting an rgb(...) color into three integers, rich verifies each channel is <= 255 and raises ColorParseError(\"color components must be <= 255 in ...\") otherwise. The regex only requires digit groups, so out-of-range values like 300 parse as integers but fail this semantic check. Negative values cannot occur (the regex has no minus sign), so this is purely an upper-bound failure.","triggerScenarios":"Color.parse(\"rgb(300,0,0)\"); scaling data to color channels with math that can exceed 255 (e.g. heat = value * 3); passing hex-derived values computed with a wrong multiplier.","commonSituations":"Data-visualization code mapping magnitudes to RGB without clamping; unit mistakes (percent 0-100 multiplied by 255 instead of 2.55); copy-pasted channel values above 255 from float calculations rounded up.","solutions":["Clamp each channel with min(255, max(0, int(x))) before formatting the rgb string","Fix the scaling factor (e.g. use int(pct * 255 / 100) for percentages)","Use round() after float scaling to avoid overshoot"],"exampleFix":"// before (Python)\nColor.parse(f\"rgb({level*3},{0},{0})\")  # level=100 -> 300 -> error\n// after\nr = min(255, level * 3)\nColor.parse(f\"rgb({r},0,0)\")","handlingStrategy":"validation","validationCode":"r, g, b = (min(255, max(0, int(c))) for c in (r, g, b))\ncolor_str = f\"rgb({r},{g},{b})\"","typeGuard":"def is_in_range_channel(v: int) -> bool:\n    return isinstance(v, int) and 0 <= v <= 255","tryCatchPattern":null,"preventionTips":["Clamp every channel to 0-255 before formatting","Use int(pct * 255 / 100) for percentage scaling and round() after float math","Add unit tests for boundary values (0, 255, 256) in color-mapping code"],"tags":["python","rich","color","rgb","range","colorparseerror"],"backgroundTag":null,"analyzedSha":"9d8f9a372cc5916fd4781fec207ced7ddac2f08f","analyzedAt":"2026-08-15T03:30:11.781Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}