{"record":{"id":"0434419ace748551","repo":"iOfficeAI/OfficeCLI","slug":"unknown-midpoint-kind-badkind-valid-percenti","errorCode":null,"errorMessage":"Unknown midPoint kind '{badKind}'. Valid: percentile:<n>, percent:<n>, num:<n>, or a bare number (percentile).","messagePattern":"Unknown midPoint kind '(.+?)'\\. Valid: percentile:<n>, percent:<n>, num:<n>, or a bare number \\(percentile\\)\\.","errorType":"validation","errorClass":"System.ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Cf.cs","lineNumber":318,"sourceCode":"        // Lenient forms: \"50\", \"50%\" (percent → percentile), and prefixed\n        // \"percentile:50\" / \"percent:50\" / \"num:50\". Anything else must be\n        // rejected: an unparsed value (\"50%\", \"percentile:50\") used to land\n        // verbatim in <cfvo val=>, which passes schema validation but real\n        // Excel refuses the whole file (0x800A03EC).\n        var midPointStr = properties.GetValueOrDefault(\"midpoint\")\n            ?? properties.GetValueOrDefault(\"midPoint\")\n            ?? \"50\";\n        var midType = ConditionalFormatValueObjectValues.Percentile;\n        var midRaw = midPointStr.Trim();\n        var midColon = midRaw.IndexOf(':');\n        if (midColon > 0)\n        {\n            midType = midRaw[..midColon].Trim().ToLowerInvariant() switch\n            {\n                \"percentile\" => ConditionalFormatValueObjectValues.Percentile,\n                \"percent\" => ConditionalFormatValueObjectValues.Percent,\n                \"num\" or \"number\" => ConditionalFormatValueObjectValues.Number,\n                var badKind => throw new ArgumentException(\n                    $\"Unknown midPoint kind '{badKind}'. Valid: percentile:<n>, percent:<n>, num:<n>, or a bare number (percentile).\")\n            };\n            midRaw = midRaw[(midColon + 1)..].Trim();\n        }\n        else if (midRaw.EndsWith('%'))\n        {\n            midType = ConditionalFormatValueObjectValues.Percent;\n            midRaw = midRaw[..^1].Trim();\n        }\n        if (!double.TryParse(midRaw, System.Globalization.NumberStyles.Float,\n                System.Globalization.CultureInfo.InvariantCulture, out _))\n            throw new ArgumentException(\n                $\"Invalid midPoint '{midPointStr}': expected a number, optionally as percentile:<n> / percent:<n> / num:<n> or '<n>%'.\");\n        var colorScale = new ColorScale();\n        colorScale.Append(new ConditionalFormatValueObject { Type = ConditionalFormatValueObjectValues.Min });\n        if (midColor != null)\n            colorScale.Append(new ConditionalFormatValueObject { Type = midType, Val = midRaw });\n        colorScale.Append(new ConditionalFormatValueObject { Type = ConditionalFormatValueObjectValues.Max });","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Cf.cs#L300-L336","documentation":"Thrown by AddColorScale when the midpoint value uses a 'kind:number' prefix form but the kind is not recognized. The midPoint string is split on the first ':' and the left side must be one of: percentile, percent, num/number. The default type (no prefix, no '%') is Percentile. Rejecting an unknown prefix prevents a malformed <cfvo type=...> from being written, which would pass schema validation but make real Excel reject the file (0x800A03EC).","triggerScenarios":"Calling Add with type=colorscale and a midpoint= property formatted as '<kind>:<n>' where kind is not percentile/percent/num/number. Examples: midpoint=average:50, midpoint=median:50, midpoint=percentile:50 (valid), midpoint=foo:10 (invalid).","commonSituations":"Typing the Excel UI category name ('average', 'median') instead of the OOXML kind; using a colon when a bare number or '<n>%' was intended; copying a formula-style string into the property.","solutions":["Use a recognized prefix: percentile:<n>, percent:<n>, or num:<n> (alias number:<n>).","For a percentile midpoint, pass a bare number (midpoint=50) — Percentile is the default kind.","For a percent midpoint, use the '<n>%' suffix (midpoint=50%) instead of a prefix."],"exampleFix":"// before: midpoint=average:50\nadd /Sheet1/A1:A10 colorscale midpoint=average:50\n// after: bare number defaults to percentile\nadd /Sheet1/A1:A10 colorscale midpoint=50","handlingStrategy":"validation","validationCode":"var kinds = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { \"percentile\", \"percent\", \"num\", \"number\" };\nvar mp = properties.GetValueOrDefault(\"midpoint\") ?? properties.GetValueOrDefault(\"midPoint\") ?? \"50\";\nvar c = mp.IndexOf(':');\nif (c > 0 && !kinds.Contains(mp[..c].Trim()))\n    throw new ArgumentException($\"midPoint kind '{mp[..c]}' invalid.\");","typeGuard":"static readonly HashSet<string> MidPointKinds = new(StringComparer.OrdinalIgnoreCase)\n{ \"percentile\", \"percent\", \"num\", \"number\" };\nstatic bool IsValidMidPoint(string? s)\n{\n    if (s is null) return true;\n    var colon = s.IndexOf(':');\n    return colon <= 0 || MidPointKinds.Contains(s[..colon].Trim());\n}","tryCatchPattern":"try { return Add(path, \"colorscale\", pos, props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"midPoint kind\"))\n{ props[\"midpoint\"] = \"50\"; return Add(path, \"colorscale\", pos, props); }","preventionTips":["Use only percentile/percent/num prefixes for kind:number form.","Pass a bare number for the default Percentile kind.","Use the '<n>%' suffix instead of 'percent:<n>' for percent midpoints."],"tags":["excel","conditional-formatting","colorscale","enum-validation","argument-validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}