d2lang/d2 · error
invalid color "%s"
Error message
invalid color "%s"
What it means
Darken reduces a color's luminance by 10%. If the input is one of d2's theme color tokens (checked via IsThemeColor, e.g. B1-B6, AA2-AA5, N1-N7), it maps through a hardcoded switch; any theme-style token not covered by that switch is rejected with this error instead of falling through to CSS parsing.
Source
Thrown at lib/color/color.go:72
case colorString[0] == 'N':
switch colorString[1] {
case '1', '2':
return N1, nil
case '3':
return N2, nil
case '4':
return N3, nil
case '5':
return N4, nil
case '6':
return N5, nil
case '7':
return N6, nil
}
}
return "", fmt.Errorf("invalid color \"%s\"", colorString)
}
return darkenCSS(colorString)
}
func darkenCSS(colorString string) (string, error) {
c, err := csscolorparser.Parse(colorString)
if err != nil {
return "", err
}
h, s, l := colorful.Color{R: c.R, G: c.G, B: c.B}.Hsl()
// decrease luminance by 10%
return colorful.Hsl(h, s, l-.1).Clamped().Hex(), nil
}
func LuminanceCategory(colorString string) (string, error) {
// check if colorString matches the `url('#grad-<sha1-hash>')` format
// which is used to refer to a <linearGradient> or <radialGradient> element.View on GitHub (pinned to 0d69dca6f5)
Solutions
- Use a valid theme token: B1–B6, AA2/AA4/AA5, or N1–N7
- If passing a regular color, use CSS syntax (hex, rgb(), named) so Darken routes to darkenCSS
- Check IsThemeColor first and validate the token against the supported set
- If you added a new theme constant, extend Darken's switch
Example fix
// before
c, err := color.Darken("B9") // invalid token
// after
c, err := color.Darken("B3") // or color.Darken("#4A6FA5") Defensive patterns
Strategy: validation
Validate before calling
var themeToken = regexp.MustCompile(`^(B[1-6]|AA[245]|N[1-7])$`)
func canDarken(s string) bool {
if themeToken.MatchString(s) {
return true
}
_, err := csscolorparser.Parse(s)
return err == nil
} Type guard
func isThemeColor(s string) bool {
switch {
case strings.HasPrefix(s, "B") && len(s) == 2 && s[1] >= '1' && s[1] <= '6':
return true
case strings.HasPrefix(s, "AA") && (s[2] == '2' || s[2] == '4' || s[2] == '5'):
return true
case strings.HasPrefix(s, "N") && s[1] >= '1' && s[1] <= '7':
return true
}
return false
} Try / catch
dark, err := color.Darken(input)
if err != nil {
log.Printf("cannot darken %q, using original: %v", input, err)
dark = input
} Prevention
- Validate theme tokens against the supported set (B1–B6, AA2/AA4/AA5, N1–N7)
- For arbitrary colors use CSS syntax so darkenCSS handles them
- If adding theme constants, extend Darken's switch in the same change
When it happens
Trigger: Calling lib/color.Darken with a theme color token whose prefix/number is not in the switch (e.g. B7, AA1, N8, or a malformed token like 'X2'), or a non-theme string shorter than the checks.
Common situations: Typoed theme color names in diagrams/styles, new theme colors added upstream but not to Darken's switch, or users passing arbitrary color strings they assumed were theme tokens.
Related errors
- expected "stroke" to be a valid named color ("orange"), a he
- expected "fill" to be a valid named color ("orange"), a hex
- invalid gradient syntax
- no parameters in gradient
- cannot parse hex color %v
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/ac2973c589f28ff9.
Report an issue: GitHub.