iOfficeAI/OfficeCLI · warning · ArgumentException

Unknown color transform '{name}'. Valid: lumMod, lumOff, sha

Error message

Unknown color transform '{name}'. Valid: lumMod, lumOff, shade, tint, satMod, satOff, hueMod, hueOff, alpha.

What it means

ParseColorTransformSuffix scans each token's leading letter run as the transform name and checks it against KnownTransforms ({lumMod, lumOff, shade, tint, satMod, satOff, hueMod, hueOff, alpha}, case-insensitive). An unrecognized name is rejected rather than silently dropped, so a typo doesn't produce a no-op color. The valid set is listed in the message.

Source

Thrown at src/officecli/Core/DrawingColorBuilder.cs:114

    {
        var result = new List<(string Name, int Val)>();
        foreach (var token in chain.Split('+', StringSplitOptions.RemoveEmptyEntries))
        {
            // Two accepted forms:
            //   "lumMod75"        — Get's canonical round-trip form, percent 0..100
            //   "lumMod=75000"    — raw OOXML percentage 0..100000
            //                       (matches the literal a:lumMod@val attribute,
            //                        what users see in PowerPoint XML / docs)
            // Both end up encoded as @val="75000" on the OOXML child. The name is
            // a leading run of letters; the remainder is '='?<signed-int>. Scan the
            // name by letters (not "first digit") so a leading '-' on the value
            // stays with the value instead of being folded into the name.
            int i = 0;
            while (i < token.Length && char.IsLetter(token[i])) i++;
            if (i == 0 || i == token.Length) continue;
            var name = token.Substring(0, i);
            if (!KnownTransforms.Contains(name))
                throw new ArgumentException(
                    $"Unknown color transform '{name}'. Valid: lumMod, lumOff, shade, tint, satMod, satOff, hueMod, hueOff, alpha.");
            bool eqForm = token[i] == '=';
            string numText = eqForm ? token.Substring(i + 1) : token.Substring(i);
            if (!int.TryParse(numText, out var raw))
                throw new ArgumentException(
                    $"Invalid color transform '{token}': value must be an integer.");
            // OOXML splits the transforms into two schema types:
            //   shade / tint / alpha  → ST_PositiveFixedPercentage (0..100%),
            //                           negatives forbidden.
            //   lumMod / lumOff / satMod / satOff / hueMod / hueOff
            //                         → ST_Percentage: SIGNED and may exceed 100%
            //                           (e.g. satMod200% to over-saturate, or
            //                           satOff-10% to desaturate). Rejecting
            //                           negatives wrongly aborted replay of the
            //                           round-trip form Get emits for these
            //                           (satOff val="-10000" → "satOff-10").
            // Only the fixed-percentage family stays clamped 0..100.
            bool fixedPct = name.ToLowerInvariant() is "shade" or "tint" or "alpha";

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Use one of the nine valid names exactly: lumMod, lumOff, shade, tint, satMod, satOff, hueMod, hueOff, alpha (case-insensitive).
  2. Check the spelling — 'lummode' vs 'lumMod', 'tintt' vs 'tint'.
  3. Map the desired effect to the closest OOXML transform (e.g. 'brightness' -> lumMod/lumOff).

Example fix

// before — unsupported name
fill="red lummode50"

// after — canonical OOXML name
fill="red lumMod50"
Defensive patterns

Strategy: validation

Validate before calling

static readonly HashSet<string> ValidTransforms = new(StringComparer.OrdinalIgnoreCase)
    { "lumMod","lumOff","shade","tint","satMod","satOff","hueMod","hueOff","alpha" };
static bool IsValidTransformName(string token)
{
    int i = 0; while (i < token.Length && char.IsLetter(token[i])) i++;
    return i > 0 && ValidTransforms.Contains(token.Substring(0, i));
}

Type guard

static bool IsValidColorTransform(string token)
{
    int i = 0; while (i < token.Length && char.IsLetter(token[i])) i++;
    if (i == 0 || i == token.Length) return false;
    if (!ValidTransforms.Contains(token.Substring(0, i))) return false;
    var rest = token[i] == '=' ? token.Substring(i + 1) : token.Substring(i);
    return int.TryParse(rest, out _);
}

Try / catch

try { DrawingColorBuilder.Build(color); }
catch (ArgumentException ex) when (ex.Message.StartsWith("Unknown color transform", StringComparison.Ordinal))
{ errors.Add(ex.Message); /* surface typo to user */ }

Prevention

When it happens

Trigger: A color suffix token like 'lumMod50%' where the letter prefix isn't one of the nine known transforms — e.g. 'brightness50', 'mod50', 'lum50', a misspelled 'lummode'. The name is everything up to the first non-letter.

Common situations: User writes a color transform using a PowerPoint-UI name or a different library's vocabulary instead of the OOXML transform names; typo in a round-tripped value; copying a transform name from docs that uses a different casing/spelling.

Related errors


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