{"record":{"id":"32fd06a7713e3641","repo":"iOfficeAI/OfficeCLI","slug":"invalid-color-transform-token-raw-value-raw-32fd06","errorCode":null,"errorMessage":"Invalid color transform '{token}': raw value {raw} below 1000 truncates to 0%; use percentage form '{name}{raw / 1000}' or raw magnitude >= 1000.","messagePattern":"Invalid color transform '(.+?)': raw value (.+?) below 1000 truncates to 0%; use percentage form '(.+?)(.+?)' or raw magnitude >= 1000\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"warning","filePath":"src/officecli/Core/DrawingColorBuilder.cs","lineNumber":148,"sourceCode":"            //                           (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;\n            if (eqForm)\n            {\n                if (raw < minRaw || raw > maxRaw)\n                    throw new ArgumentException(\n                        $\"Invalid color transform '{token}': raw value {raw} out of range {minRaw}-{maxRaw}.\");\n                // OOXML raw units are 1/1000 of a percent. Integer division\n                // truncates values whose magnitude is 1..999 to 0 (lumMod=75 raw\n                // → 0 instead of 7.5%). Reject sub-1000 magnitudes so callers\n                // can't silently get a no-op; the percentage form covers that range.\n                if (raw != 0 && Math.Abs(raw) < 1000)\n                    throw new ArgumentException(\n                        $\"Invalid color transform '{token}': raw value {raw} below 1000 truncates to 0%; use percentage form '{name}{raw / 1000}' or raw magnitude >= 1000.\");\n                pct = raw / 1000;\n            }\n            else\n            {\n                if (raw < minPct || raw > maxPct)\n                    throw new ArgumentException(\n                        $\"Invalid color transform '{token}': percentage {raw} out of range {minPct}-{maxPct}.\");\n                pct = raw;\n            }\n            // Canonicalize: lumMod → lumMod (lowercase first letter? OOXML uses\n            // camelCase: lumMod, lumOff, satMod, satOff, hueMod, hueOff,\n            // shade, tint). KnownTransforms matches case-insensitively; we\n            // re-emit the canonical form here.\n            var canonical = name.ToLowerInvariant() switch\n            {\n                \"lummod\" => \"lumMod\",\n                \"lumoff\" => \"lumOff\",","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/DrawingColorBuilder.cs#L130-L166","documentation":"In eqForm, the raw integer had magnitude 1..999. Because OOXML raw units are 1/1000 of a percent, integer division (raw/1000) truncates such values to 0 — a silent no-op. The parser rejects these and points the user to the percentage form (e.g. 'lumMod75'), which covers that magnitude correctly.","triggerScenarios":"A small raw eqForm value like 'lumMod=75' (intending 7.5% but meaning 0.075%), 'shade=500', or 'tint=999'. raw != 0 and Math.Abs(raw) < 1000 trips the guard.","commonSituations":"User thinks the eqForm number is a percentage and writes 'lumMod=75' meaning 75%, when 75 raw = 0.075% (and truncates to 0). The message suggests the percentage equivalent '{name}{raw/1000}'.","solutions":["Switch to percentage form: 'lumMod75' for 75% instead of 'lumMod=75'.","If raw OOXML is genuinely intended, use magnitude >= 1000 (e.g. 'lumMod=75000' for 75%)."],"exampleFix":"// before — small raw value silently truncates to 0%\nfill=\"red lumMod=75\"\n\n// after — percentage form (75%)\nfill=\"red lumMod75\"","handlingStrategy":"validation","validationCode":"// For eqForm, require raw magnitude 0 or >= 1000\nstatic bool RawTruncatesToZero(int raw) => raw != 0 && Math.Abs(raw) < 1000;\n// If RawTruncatesToZero(raw), suggest percentage form \"{name}{raw/1000}\".","typeGuard":null,"tryCatchPattern":"try { DrawingColorBuilder.Build(color); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"truncates to 0%\", StringComparison.Ordinal))\n{ errors.Add(ex.Message); }","preventionTips":["Don't put small raw values in eqForm — they silently become 0%.","Use percentage form for values under 100%.","Reserve eqForm for true OOXML raw magnitudes (>= 1000)."],"tags":["color","transform","validation","truncation","user-input"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}