{"record":{"id":"10165ae32773a3ef","repo":"iOfficeAI/OfficeCLI","slug":"invalid-color-value-original-hsl-saturation-10165a","errorCode":null,"errorMessage":"Invalid color value: '{original}'. HSL saturation/lightness must be 0%-100%.","messagePattern":"Invalid color value: '(.+?)'\\. HSL saturation/lightness must be 0%-100%\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/ParseHelpers.cs","lineNumber":213,"sourceCode":"        // — turn/rad/grad — are out of scope for the input vocabulary we care\n        // about and would just need their own multipliers if asked for).\n        if (token.EndsWith(\"deg\", StringComparison.OrdinalIgnoreCase))\n            token = token[..^3].Trim();\n        if (!double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var h))\n            throw new ArgumentException($\"Invalid color value: '{original}'. Hue must be a number in degrees.\");\n        h %= 360;\n        if (h < 0) h += 360;\n        return h;\n    }\n\n    private static double ParsePercent01(string token, string original)\n    {\n        token = token.Trim();\n        if (!token.EndsWith('%'))\n            throw new ArgumentException($\"Invalid color value: '{original}'. HSL saturation/lightness must be expressed as a percentage (e.g. 50%).\");\n        if (!double.TryParse(token[..^1], NumberStyles.Float, CultureInfo.InvariantCulture, out var pct)\n            || pct < 0 || pct > 100)\n            throw new ArgumentException($\"Invalid color value: '{original}'. HSL saturation/lightness must be 0%-100%.\");\n        return pct / 100.0;\n    }\n\n    private static (byte R, byte G, byte B) HslToRgb(double h, double s, double l)\n    {\n        // Standard HSL → RGB (CSS Color Module Level 3).\n        double c = (1 - Math.Abs(2 * l - 1)) * s;\n        double hp = h / 60.0;\n        double x = c * (1 - Math.Abs(hp % 2 - 1));\n        double r1 = 0, g1 = 0, b1 = 0;\n        if (hp < 1)      { r1 = c; g1 = x; b1 = 0; }\n        else if (hp < 2) { r1 = x; g1 = c; b1 = 0; }\n        else if (hp < 3) { r1 = 0; g1 = c; b1 = x; }\n        else if (hp < 4) { r1 = 0; g1 = x; b1 = c; }\n        else if (hp < 5) { r1 = x; g1 = 0; b1 = c; }\n        else             { r1 = c; g1 = 0; b1 = x; }\n        double m = l - c / 2;\n        return (","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/ParseHelpers.cs#L195-L231","documentation":"Thrown by ParsePercent01 when an HSL saturation or lightness percentage parses as a number but is outside 0%-100%. Both channels are clamped-free in CSS terms here — the library rejects out-of-range rather than silently clipping, to surface bad color math.","triggerScenarios":"Passing hsl(120,150%,50%) or hsl(120,50%,-10%). A saturation/lightness computed from a gradient or blend that exceeded bounds.","commonSituations":"HSL manipulation code that adds saturation without clamping; UI sliders allowing >100%; converting from another color space whose gamut exceeds HSL.","solutions":["Clamp S and L to [0,100] before formatting.","Audit color-space conversion code for unbounded outputs.","Round and clamp in one step: Math.Clamp(Math.Round(v),0,100)."],"exampleFix":"// before\nvar c = $\"hsl({h},{s}%,{l}%\"); // s == 150\nParseColor(c); // throws 290\n\n// after\nint C(double v) => (int)Math.Clamp(Math.Round(v),0,100);\nvar c = $\"hsl({h},{C(s)}%,{C(l)}%)\");\nParseColor(c);","handlingStrategy":"validation","validationCode":"static string ClampSl(double v) => $\"{Math.Clamp(Math.Round(v),0,100)}%\";\n// build: $\"hsl({h},{ClampSl(s)},{ClampSl(l)})\"","typeGuard":"static bool IsValidSlPct(string token)\n    => token.EndsWith('%')\n       && double.TryParse(token[..^1], CultureInfo.InvariantCulture, out var v)\n       && v >= 0 && v <= 100;","tryCatchPattern":"try { color = ParseColor(spec); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"must be 0%-100%\"))\n{ /* clamp S/L and retry */ }","preventionTips":["Clamp HSL S/L to [0,100]% after any color math.","Audit color-space conversions for out-of-gamut outputs.","Round and clamp in one step to avoid drift."],"tags":["color","validation","argument","hsl","range"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}