MaterialDesignInXAML/MaterialDesignInXamlToolkit · error · InvalidOperationException

Invalid HSB values

Error message

Invalid HSB values

What it means

`HsbExtensions.ToColor` converts an HSB color to a WPF `Color` via a six-armed switch on the sector index `i` (0-5). The default arm `throw new InvalidOperationException("Invalid HSB values")` fires when `i` is outside 0-5. After normalizing hue into [0,360) and dividing by 60, `i = (int)Math.Floor(h)` should be 0-5, but a hue of exactly `360.0` slips past the epsilon-tolerant `h.IsCloseTo(360)` guard (0 < double.Epsilon is false) and yields `i = 6`.

Source

Thrown at src/MaterialDesignColors.Wpf/ColorManipulation/HsbExtensions.cs:37

        while (h < 0) h += 360;

        h /= 60;

        int i = (int)Math.Floor(h);
        double f = h - i;
        double p = b * (1 - s);
        double q = b * (1 - s * f);
        double t = b * (1 - s * (1 - f));

        return i switch
        {
            0 => Color.FromRgb((byte)b, (byte)t, (byte)p),
            1 => Color.FromRgb((byte)q, (byte)b, (byte)p),
            2 => Color.FromRgb((byte)p, (byte)b, (byte)t),
            3 => Color.FromRgb((byte)p, (byte)q, (byte)b),
            4 => Color.FromRgb((byte)t, (byte)p, (byte)b),
            5 => Color.FromRgb((byte)b, (byte)p, (byte)q),
            _ => throw new InvalidOperationException("Invalid HSB values"),
        };
    }

    public static Hsb ToHsb(this Color color)
    {
        double r = color.R;
        double g = color.G;
        double b = color.B;

        r /= 255;
        g /= 255;
        b /= 255;

        double[] rgb = [r, g, b];
        double max = rgb.Max();
        double min = rgb.Min();
        double v = max;
        double h = max;

View on GitHub (pinned to 98edec3a0b)

Solutions

  1. Normalize hue to [0,360) yourself before calling ToColor: `hue %= 360; if (hue < 0) hue += 360;` and avoid passing exactly 360.
  2. Upgrade the library / patch `ToColor` so the 360 guard is inclusive (e.g. `if (h >= 360) h = 0;` after the wrap loops) and/or extend the switch to handle `i == 6` as sector 0.
  3. Validate inputs: reject NaN/infinity and clamp hue to [0,360).
  4. If consuming the library as-is, wrap the call and on failure retry with `hue = 0`.

Example fix

// before
var c = new Hsb(360.0, 1.0, 1.0).ToColor(); // throws Invalid HSB values

// after (caller clamp)
double hue = 360.0 % 360;
if (hue < 0) hue += 360;
var c = new Hsb(hue, 1.0, 1.0).ToColor();
Defensive patterns

Strategy: validation

Validate before calling

double SafeHue(double hue)
{
    if (double.IsNaN(hue) || double.IsInfinity(hue)) hue = 0;
    hue %= 360;
    if (hue < 0) hue += 360;
    return hue; // strictly [0,360)
}
var c = new Hsb(SafeHue(inputHue), sat, bright).ToColor();

Type guard

static bool IsValidHsb(in Hsb h) =>
    !double.IsNaN(h.Hue) && !double.IsInfinity(h.Hue) &&
    h.Hue >= 0 && h.Hue < 360 &&
    h.Saturation >= 0 && h.Saturation <= 1 &&
    h.Brightness >= 0 && h.Brightness <= 1;

Try / catch

try { color = hsb.ToColor(); }
catch (InvalidOperationException)
{
    // edge: hue landed exactly on 360; retry wrapped
    color = new Hsb(0, hsb.Saturation, hsb.Brightness).ToColor();
}

Prevention

When it happens

Trigger: Calling `ToColor` on an `Hsb` whose `Hue` is exactly `360.0` (or any value the normalization leaves at/above 360); constructing an Hsb from code/round-tripping a color whose computed hue lands exactly on the 360 boundary; NaN or infinity in saturation/brightness interacting with the floor.

Common situations: Red colors whose `ToHsb` round-trip produces hue 0/360 boundary values; feeding user-provided HSB triples from a color picker; using a hue slider that allows 360 as its maximum.

Related errors


AI-assisted analysis of MaterialDesignInXAML/MaterialDesignInXamlToolkit@98edec3a0b (2026-08-13). Data as JSON: /api/errors/b9210877295a7477. Report an issue: GitHub.