kovidgoyal/kitty · error
not a valid color name: %#v %w
Error message
not a valid color name: %#v %w
What it means
Raised by ParseColor when a structured parser WAS selected (hex or slash format) but that parser returned an error, which is then wrapped with the original input for context. The inner error is one of the length/number errors from parse_sharp, parse_rgb or parse_rgbi; this is the top-level wrapper a caller actually sees.
Source
Thrown at tools/utils/style/wrapper.go:240
}
case 'r':
if len(raw) > 4 && strings.HasPrefix(raw, "rgb") {
raw = raw[3:]
switch raw[0] {
case ':':
parser, raw = parse_rgb, raw[1:]
case 'i':
if strings.HasPrefix(raw, "i:") {
parser, raw = parse_rgbi, raw[2:]
}
}
}
}
if parser == nil {
err = fmt.Errorf("not a valid color name: %#v", color)
} else {
if ans, err = parser(raw); err != nil {
err = fmt.Errorf("not a valid color name: %#v %w", color, err)
}
}
return
}
type NullableColor struct {
Color RGBA
IsSet bool
}
func ParseColorOrNone(color string) (NullableColor, error) {
raw := strings.TrimSpace(strings.ToLower(color))
if raw == "none" {
return NullableColor{}, nil
}
c, err := ParseColor(raw)
return NullableColor{Color: c, IsSet: err == nil}, err
}View on GitHub (pinned to 6d5d0c4406)
Solutions
- Read the wrapped cause (err via errors.Unwrap) to identify whether it's a length or numeric problem, and fix the value accordingly.
- Normalize color input to canonical #rrggbb hex before passing it in.
- Wrap ParseColor calls and report both the offending value and the cause to the user/config layer.
- Add a pre-validation regex/allow-list for user-supplied colors.
Example fix
// before
if _, err := style.ParseColor(userColor); err != nil {
return err // "not a valid color name: \"#ff00\" length not a multiple of 3"
}
// after
var hexRe = regexp.MustCompile(`^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$`)
if !hexRe.MatchString(userColor) {
userColor = "#ffffff" // or reject with a clear message
}
if _, err := style.ParseColor(userColor); err != nil {
return fmt.Errorf("bad color %q: %w", userColor, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
var colorRe = regexp.MustCompile(`^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$`)
func sanitizeColor(s string) (string, bool) {
s = strings.TrimSpace(strings.ToLower(s))
return s, colorRe.MatchString(s)
} Type guard
func isParseableColor(s string) bool {
s = strings.TrimSpace(strings.ToLower(s))
if _, ok := style.ColorNames[s]; ok { return true }
return strings.HasPrefix(s, "#") && len(s) > 1 && (len(s)-1)%3 == 0
} Try / catch
ans, err := style.ParseColor(input)
if err != nil {
var cause error = errors.Unwrap(err) // inner length/number error
log.Printf("bad color %q (%v); falling back", input, cause)
ans, err = style.ParseColor("#ffffff")
if err != nil { return err } // default must be valid
} Prevention
- Centralize color parsing behind one helper with fallback.
- Use errors.Unwrap/errors.Is to surface the root cause in logs.
- Unit-test ParseColor with the exact values your config accepts.
When it happens
Trigger: Any of: ParseColor("#ff00") (bad hex length), ParseColor("#gg0000") (non-hex digits), a slash format with wrong part count or non-numeric/out-of-range parts. The wrapping preserves the cause via %w, so errors.Is/Unwrap can inspect it.
Common situations: User-facing color settings fields (set_color_in_color_map, ColorSettingsAsEscapeCodes, Set) receiving free-form text; config/theme files with slightly malformed values; the comment-stripping logic leaving a partially-valid string.
Related errors
- length not a multiple of 3
- not a valid color name: %#v
- Invalid color name: {raw!r}
- invalid rgb numbers
- Unknown type_of_input: {type_of_input}
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/dacca8eb6687c9b3.
Report an issue: GitHub.