iOfficeAI/OfficeCLI · error · System.ArgumentException

Invalid midPoint '{midPointStr}': expected a number, optiona

Error message

Invalid midPoint '{midPointStr}': expected a number, optionally as percentile:<n> / percent:<n> / num:<n> or '<n>%'.

What it means

Thrown by AddColorScale after the midPoint kind prefix (or '%' suffix) is stripped: the remaining substring must parse as a double using invariant culture. This guards the <cfvo val=...> attribute so a non-numeric value never reaches the OOXML output, which would make Excel refuse the workbook. The original full midPoint string is echoed in the message for diagnosis.

Source

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

        {
            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 });
        colorScale.Append(new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = normalizedMinColor });
        if (midColor != null)
        {
            var normalizedMidColor = ParseHelpers.NormalizeArgbColor(midColor);
            colorScale.Append(new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = normalizedMidColor });
        }
        colorScale.Append(new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = normalizedMaxColor });

        var csRule = new ConditionalFormattingRule
        {
            Type = ConditionalFormatValues.ColorScale,
            Priority = NextCfPriority(GetSheet(csWorksheet))

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Provide a numeric value: midpoint=50, midpoint=percentile:50, midpoint=percent:25, or midpoint=50%.
  2. Use a period as the decimal separator (invariant culture parsing) — e.g. midpoint=12.5 not midpoint=12,5.
  3. If omitting the midpoint, also omit the midcolor property so the rule becomes a 2-color scale.

Example fix

// before: midpoint=percentile:fifty
add /Sheet1/A1:A10 colorscale midpoint=percentile:fifty
// after: numeric value
add /Sheet1/A1:A10 colorscale midpoint=percentile:50
Defensive patterns

Strategy: validation

Validate before calling

var mp = (properties.GetValueOrDefault("midpoint") ?? properties.GetValueOrDefault("midPoint") ?? "50").Trim();
var colon = mp.IndexOf(':');
var numPart = colon > 0 ? mp[(colon + 1)..].Trim() : (mp.EndsWith('%') ? mp[..^1].Trim() : mp);
if (!double.TryParse(numPart, NumberStyles.Float, CultureInfo.InvariantCulture, out _))
    throw new ArgumentException($"midPoint '{mp}' numeric portion invalid.");

Type guard

static bool IsValidMidPointNumber(string? s)
{
    if (s is null) return true;
    var mp = s.Trim();
    var colon = mp.IndexOf(':');
    var part = colon > 0 ? mp[(colon + 1)..].Trim() : (mp.EndsWith('%') ? mp[..^1].Trim() : mp);
    return double.TryParse(part, NumberStyles.Float, CultureInfo.InvariantCulture, out _);
}

Try / catch

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

Prevention

When it happens

Trigger: Calling Add with type=colorscale and a midpoint= whose payload (after prefix/suffix handling) is not a floating-point number. Examples: midpoint=percentile:fifty, midpoint=high, midpoint=num:, midpoint=50%% (double percent).

Common situations: Typo or stray characters in the number portion; locale confusion where a comma decimal separator is used instead of a period; passing an empty value after a valid prefix.

Related errors


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