kovidgoyal/kitty · error
length not a multiple of 3
Error message
length not a multiple of 3
What it means
Raised by parse_sharp in tools/utils/style/wrapper.go when parsing a #-prefixed hex color string whose remainder (after stripping '#') has a length that is not divisible by 3. The parser splits the hex digits into three equal parts for R, G and B channels, so only balanced forms like #rgb, #rrggbb, #rrrgggbbb, #rrrrggggbbbb are accepted. Any other length (e.g. 4, 5, 7 hex digits) is rejected before numeric parsing.
Source
Thrown at tools/utils/style/wrapper.go:152
}
prefix = append(prefix, strconv.Itoa(number_base+num))
} else {
prefix = append(prefix, fmt.Sprintf("%d:5:%d", number_base+8, num))
}
} else {
prefix = append(prefix, fmt.Sprintf("%d:2:%d:%d:%d", number_base+8, self.val.Red, self.val.Green, self.val.Blue))
}
return prefix, suffix
}
type color_value struct {
is_set bool
val color_type
}
func parse_sharp(color string) (ans RGBA, err error) {
if len(color)%3 != 0 {
return RGBA{}, fmt.Errorf("length not a multiple of 3")
}
part_size := len(color) / 3
r, g, b := color[:part_size], color[part_size:2*part_size], color[part_size*2:part_size*3]
if !ans.parse_rgb_strings(r, g, b) {
err = fmt.Errorf("invalid rgb numbers")
}
return
}
func parse_rgb(color string) (ans RGBA, err error) {
colors := strings.Split(color, "/")
if len(colors) != 3 {
return RGBA{}, fmt.Errorf("length not a multiple of 3")
}
if ans.parse_rgb_strings(colors[0], colors[1], colors[2]) {
return
}
err = fmt.Errorf("invalid rgb numbers")View on GitHub (pinned to 6d5d0c4406)
Solutions
- Correct the hex color to a 3-divisible form: #rgb, #rrggbb (most common), or longer equal-part forms.
- If you intended RGBA with alpha, drop the alpha component — this parser supports only R/G/B equal-length parts.
- Validate user-supplied colors with a regex like ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$ before passing them to ParseColor.
- Check for stray characters or comments appended to the color string before the parse.
Example fix
// before
col, err := style.ParseColor("#ff00") // length 4, not a multiple of 3
// after
col, err := style.ParseColor("#ff0000") // 6 hex digits, valid rrggbb Defensive patterns
Strategy: validation
Validate before calling
var hexColorRe = regexp.MustCompile(`^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{9}|[0-9a-fA-F]{12})$`)
func validHexColor(s string) bool { return hexColorRe.MatchString(strings.TrimSpace(s)) } Type guard
func isHexColor(s string) bool {
s = strings.TrimPrefix(strings.TrimSpace(strings.ToLower(s)), "#")
return len(s)%3 == 0 && len(s) > 0
} Prevention
- Normalize all colors to #rrggbb before passing to ParseColor.
- Reject user color input early with a regex instead of relying on ParseColor errors.
- Write tests covering 3/4/5/7-digit hex inputs.
When it happens
Trigger: Calling ParseColor (or a style helper like to_color / Set / parse_background) with a malformed hex color such as "#ff00" (4 digits), "#ff000" (5 digits) or "#fffffff" (7 digits). Note ParseColor lowercases and strips inline comments, but still requires the remaining hex portion after '#' to divide evenly by 3.
Common situations: Typos in color literals (missing or extra digit), copying 4-digit RGBA hex (#ff0f → 'ff0f' is 4 chars, invalid here since this parser has no alpha channel), or truncation of config values like "#ff00" instead of "#ff0000" in theme/settings files.
Related errors
- invalid rgb numbers
- not a valid color name: %#v
- not a valid color name: %#v %w
- Invalid color name: {raw!r}
- Unknown type_of_input: {type_of_input}
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/8a4a13622d075abc.
Report an issue: GitHub.