{"record":{"id":"c260fca85fd0dfcb","repo":"iOfficeAI/OfficeCLI","slug":"invalid-color-transform-token-value-must-be-a","errorCode":null,"errorMessage":"Invalid color transform '{token}': value must be an integer.","messagePattern":"Invalid color transform '(.+?)': value must be an integer\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"warning","filePath":"src/officecli/Core/DrawingColorBuilder.cs","lineNumber":119,"sourceCode":"            //   \"lumMod75\"        — Get's canonical round-trip form, percent 0..100\n            //   \"lumMod=75000\"    — raw OOXML percentage 0..100000\n            //                       (matches the literal a:lumMod@val attribute,\n            //                        what users see in PowerPoint XML / docs)\n            // Both end up encoded as @val=\"75000\" on the OOXML child. The name is\n            // a leading run of letters; the remainder is '='?<signed-int>. Scan the\n            // name by letters (not \"first digit\") so a leading '-' on the value\n            // stays with the value instead of being folded into the name.\n            int i = 0;\n            while (i < token.Length && char.IsLetter(token[i])) i++;\n            if (i == 0 || i == token.Length) continue;\n            var name = token.Substring(0, i);\n            if (!KnownTransforms.Contains(name))\n                throw new ArgumentException(\n                    $\"Unknown color transform '{name}'. Valid: lumMod, lumOff, shade, tint, satMod, satOff, hueMod, hueOff, alpha.\");\n            bool eqForm = token[i] == '=';\n            string numText = eqForm ? token.Substring(i + 1) : token.Substring(i);\n            if (!int.TryParse(numText, out var raw))\n                throw new ArgumentException(\n                    $\"Invalid color transform '{token}': value must be an integer.\");\n            // OOXML splits the transforms into two schema types:\n            //   shade / tint / alpha  → ST_PositiveFixedPercentage (0..100%),\n            //                           negatives forbidden.\n            //   lumMod / lumOff / satMod / satOff / hueMod / hueOff\n            //                         → ST_Percentage: SIGNED and may exceed 100%\n            //                           (e.g. satMod200% to over-saturate, or\n            //                           satOff-10% to desaturate). Rejecting\n            //                           negatives wrongly aborted replay of the\n            //                           round-trip form Get emits for these\n            //                           (satOff val=\"-10000\" → \"satOff-10\").\n            // Only the fixed-percentage family stays clamped 0..100.\n            bool fixedPct = name.ToLowerInvariant() is \"shade\" or \"tint\" or \"alpha\";\n            int maxPct = fixedPct ? 100 : 1000;       // 1000% headroom for ST_Percentage\n            int minPct = fixedPct ? 0 : -1000;        // signed for the Mod/Off family\n            int maxRaw = fixedPct ? 100000 : 1000000;\n            int minRaw = fixedPct ? 0 : -1000000;\n            int pct;","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/DrawingColorBuilder.cs#L101-L137","documentation":"The transform name was recognized, but the value portion (after the optional '=') failed int.TryParse. The parser expects a signed integer — either an OOXML raw value (eqForm, 'lumMod=75000') or a percentage ('lumMod75'). A non-integer like 'lumMod=7.5' or 'shadeabc' is rejected.","triggerScenarios":"A token like 'shade=50.5' (decimal), 'tint=abc', 'lumMod=' (empty value), or 'alpha1.5%' where the numeric part isn't a clean integer. numText is token.Substring after the letter name.","commonSituations":"User supplies a fractional percentage as a decimal ('shade=33.3') instead of an integer; pastes a value with a stray unit ('lumMod=50%'); leaves the value empty after '='.","solutions":["Provide an integer: raw OOXML form 'lumMod=75000' or percentage form 'lumMod75'.","For fractional percentages, round to the nearest integer percentage.","Drop any stray unit suffix from the value (the parser takes a bare int)."],"exampleFix":"// before — decimal value fails int parse\nfill=\"red shade=33.3\"\n\n// after — integer percentage\nfill=\"red shade33\"","handlingStrategy":"validation","validationCode":"static bool IsValidTransformValue(string token)\n{\n    int i = 0; while (i < token.Length && char.IsLetter(token[i])) i++;\n    if (i == 0 || i == token.Length) return false;\n    var rest = token[i] == '=' ? token.Substring(i + 1) : token.Substring(i);\n    return rest.Length > 0 && int.TryParse(rest, out _);\n}","typeGuard":null,"tryCatchPattern":"try { DrawingColorBuilder.Build(color); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"value must be an integer\", StringComparison.Ordinal))\n{ errors.Add(ex.Message); }","preventionTips":["Supply an integer (raw OOXML or percentage), not a decimal.","Round fractional percentages to the nearest integer.","Strip unit suffixes from transform values."],"tags":["color","transform","validation","user-input","parsing"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}