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

  1. Read the wrapped cause (err via errors.Unwrap) to identify whether it's a length or numeric problem, and fix the value accordingly.
  2. Normalize color input to canonical #rrggbb hex before passing it in.
  3. Wrap ParseColor calls and report both the offending value and the cause to the user/config layer.
  4. 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

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


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/dacca8eb6687c9b3. Report an issue: GitHub.