{"record":{"id":"d0369f8c48e77f0b","repo":"iOfficeAI/OfficeCLI","slug":"invalid-threshold-parts-0-in-colorrule-expec","errorCode":null,"errorMessage":"Invalid threshold '{parts[0]}' in colorRule. Expected a number.","messagePattern":"Invalid threshold '(.+?)' in colorRule\\. Expected a number\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/Chart/ChartHelper.Advanced.cs","lineNumber":311,"sourceCode":"    /// Three-tier: \"0:FF0000:50:FFAA00:100:00AA00\" — red/orange/green zones\n    /// </summary>\n    internal static void ApplyColorRule(C.PlotArea plotArea, string spec)\n    {\n        var parts = spec.Split(':');\n        if (parts.Length < 3)\n            throw new ArgumentException(\n                $\"Invalid colorRule '{spec}'. Expected: threshold:belowColor:aboveColor (e.g. '0:FF0000:00AA00') \" +\n                \"or low:lowColor:mid:midColor:high:highColor (e.g. '0:FF0000:50:FFAA00:100:00AA00').\");\n\n        var rules = new List<(double threshold, string color)>();\n        string topColor;\n\n        if (parts.Length == 3)\n        {\n            // Simple two-zone: threshold:belowColor:aboveColor\n            if (!double.TryParse(parts[0], System.Globalization.NumberStyles.Float,\n                System.Globalization.CultureInfo.InvariantCulture, out var t))\n                throw new ArgumentException($\"Invalid threshold '{parts[0]}' in colorRule. Expected a number.\");\n            rules.Add((t, parts[1].Trim()));\n            topColor = parts[2].Trim();\n        }\n        else\n        {\n            // Multi-zone: t1:c1:t2:c2:...:cN\n            for (int i = 0; i < parts.Length - 1; i += 2)\n            {\n                if (!double.TryParse(parts[i], System.Globalization.NumberStyles.Float,\n                    System.Globalization.CultureInfo.InvariantCulture, out var t))\n                    throw new ArgumentException($\"Invalid threshold '{parts[i]}' in colorRule.\");\n                rules.Add((t, parts[i + 1].Trim()));\n            }\n            topColor = parts.Length % 2 == 1 ? parts[^1].Trim() : rules[^1].color;\n            if (parts.Length % 2 == 0)\n                rules.RemoveAt(rules.Count - 1); // Last pair has no \"above\" — use as topColor\n        }\n","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/Chart/ChartHelper.Advanced.cs#L293-L329","documentation":"In the simple 3-part colorRule form (threshold:belowColor:aboveColor), parts[0] must be a numeric threshold. If double.TryParse fails on the first segment, the library rejects it with 'Expected a number'. This is the 3-part path specifically (parts.Length == 3); the multi-zone path has its own error (error 129).","triggerScenarios":"Passing a 3-part colorRule where the first segment is non-numeric: 'abc:FF0000:00AA00' or 'N/A:FF0000:00AA00'. The spec must have exactly 3 colon-separated parts.","commonSituations":"Using locale-specific number formats with comma decimals ('0,5:FF0000:00AA00'). Placing a color in the threshold slot by mistake. Using a non-numeric sentinel like 'avg' or 'median' (not supported — only literal numbers).","solutions":["Ensure the first segment in a 3-part colorRule is a plain number: '0:FF0000:00AA00'.","Use a dot as the decimal separator: '0.5' not '0,5'.","Use numeric values only — the library does not compute aggregates like average or median."],"exampleFix":"// before\ncolorrule: \"abc:FF0000:00AA00\"\n// after\ncolorrule: \"0:FF0000:00AA00\"","handlingStrategy":"validation","validationCode":"static bool IsThresholdNumeric3Part(string spec)\n{\n    var parts = spec.Split(':');\n    return parts.Length != 3 || double.TryParse(parts[0],\n        System.Globalization.NumberStyles.Float,\n        System.Globalization.CultureInfo.InvariantCulture, out _);\n}","typeGuard":null,"tryCatchPattern":"try { ChartHelperAdvanced.ApplyColorRule(plotArea, spec); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"Invalid threshold\") && ex.Message.Contains(\"Expected a number\"))\n{\n    Console.Error.WriteLine($\"Threshold in 3-part colorRule is non-numeric. Use a plain number.\");\n}","preventionTips":["Use invariant (dot-decimal) number format for thresholds.","Place the numeric threshold as the first segment.","Do not use aggregate names like 'avg' or 'median' — only literal numbers."],"tags":["chart","conditional-coloring","colorrule","number-parsing"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}