iOfficeAI/OfficeCLI · error · System.ArgumentException

Unknown midPoint kind '{badKind}'. Valid: percentile:<n>, pe

Error message

Unknown midPoint kind '{badKind}'. Valid: percentile:<n>, percent:<n>, num:<n>, or a bare number (percentile).

What it means

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).

Source

Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.Cf.cs:318

        // Lenient forms: "50", "50%" (percent → percentile), and prefixed
        // "percentile:50" / "percent:50" / "num:50". Anything else must be
        // rejected: an unparsed value ("50%", "percentile:50") used to land
        // verbatim in <cfvo val=>, which passes schema validation but real
        // Excel refuses the whole file (0x800A03EC).
        var midPointStr = properties.GetValueOrDefault("midpoint")
            ?? properties.GetValueOrDefault("midPoint")
            ?? "50";
        var midType = ConditionalFormatValueObjectValues.Percentile;
        var midRaw = midPointStr.Trim();
        var midColon = midRaw.IndexOf(':');
        if (midColon > 0)
        {
            midType = midRaw[..midColon].Trim().ToLowerInvariant() switch
            {
                "percentile" => ConditionalFormatValueObjectValues.Percentile,
                "percent" => ConditionalFormatValueObjectValues.Percent,
                "num" or "number" => ConditionalFormatValueObjectValues.Number,
                var badKind => throw new ArgumentException(
                    $"Unknown midPoint kind '{badKind}'. Valid: percentile:<n>, percent:<n>, num:<n>, or a bare number (percentile).")
            };
            midRaw = midRaw[(midColon + 1)..].Trim();
        }
        else if (midRaw.EndsWith('%'))
        {
            midType = ConditionalFormatValueObjectValues.Percent;
            midRaw = midRaw[..^1].Trim();
        }
        if (!double.TryParse(midRaw, System.Globalization.NumberStyles.Float,
                System.Globalization.CultureInfo.InvariantCulture, out _))
            throw new ArgumentException(
                $"Invalid midPoint '{midPointStr}': expected a number, optionally as percentile:<n> / percent:<n> / num:<n> or '<n>%'.");
        var colorScale = new ColorScale();
        colorScale.Append(new ConditionalFormatValueObject { Type = ConditionalFormatValueObjectValues.Min });
        if (midColor != null)
            colorScale.Append(new ConditionalFormatValueObject { Type = midType, Val = midRaw });
        colorScale.Append(new ConditionalFormatValueObject { Type = ConditionalFormatValueObjectValues.Max });

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Use a recognized prefix: percentile:<n>, percent:<n>, or num:<n> (alias number:<n>).
  2. For a percentile midpoint, pass a bare number (midpoint=50) — Percentile is the default kind.
  3. For a percent midpoint, use the '<n>%' suffix (midpoint=50%) instead of a prefix.

Example fix

// before: midpoint=average:50
add /Sheet1/A1:A10 colorscale midpoint=average:50
// after: bare number defaults to percentile
add /Sheet1/A1:A10 colorscale midpoint=50
Defensive patterns

Strategy: validation

Validate before calling

var kinds = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "percentile", "percent", "num", "number" };
var mp = properties.GetValueOrDefault("midpoint") ?? properties.GetValueOrDefault("midPoint") ?? "50";
var c = mp.IndexOf(':');
if (c > 0 && !kinds.Contains(mp[..c].Trim()))
    throw new ArgumentException($"midPoint kind '{mp[..c]}' invalid.");

Type guard

static readonly HashSet<string> MidPointKinds = new(StringComparer.OrdinalIgnoreCase)
{ "percentile", "percent", "num", "number" };
static bool IsValidMidPoint(string? s)
{
    if (s is null) return true;
    var colon = s.IndexOf(':');
    return colon <= 0 || MidPointKinds.Contains(s[..colon].Trim());
}

Try / catch

try { return Add(path, "colorscale", pos, props); }
catch (ArgumentException ex) when (ex.Message.Contains("midPoint kind"))
{ props["midpoint"] = "50"; return Add(path, "colorscale", pos, props); }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/0434419ace748551. Report an issue: GitHub.