{"record":{"id":"70c6b41be36647dd","repo":"iOfficeAI/OfficeCLI","slug":"invalid-paramname-value-raw-empty","errorCode":null,"errorMessage":"Invalid {paramName} value: '{raw}' (empty).","messagePattern":"Invalid (.+?) value: '(.+?)' \\(empty\\)\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"warning","filePath":"src/officecli/Core/DrawingEffectsHelper.cs","lineNumber":343,"sourceCode":"    {\n        var existing = colorElement.GetFirstChild<Drawing.Alpha>();\n        if (existing != null) existing.Remove();\n        if (alphaVal == 100000) return;\n        colorElement.AppendChild(new Drawing.Alpha { Val = alphaVal });\n    }\n\n    private static double ParseParam(string[] parts, int index, double defaultValue, string paramName)\n    {\n        if (parts.Length <= index) return defaultValue;\n        var raw = parts[index];\n        // The historical contract is \"bare double\" — blur/dist in pt, angle\n        // in deg, opacity in %. Accept unit-qualified inputs that match each\n        // dimension so callers can write \"5pt\", \"45deg\", \"40%\" without\n        // forcing them to know the internal unit. Strip and parse the\n        // numeric prefix; reject unknown trailing letters.\n        var num = raw.Trim();\n        if (num.Length == 0)\n            throw new ArgumentException($\"Invalid {paramName} value: '{raw}' (empty).\");\n        // Strip a trailing alpha unit suffix (pt/deg/%/cm/in/px/emu). The\n        // numeric routes through pt/deg/% as-is — units other than pt for a\n        // pt-dimension still parse the number but the result is not\n        // converted; agents should stick to bare numbers or the native unit\n        // for now. The point of this fix is to stop ParseParam throwing on\n        // a unit-qualified token; a future pass can do real unit conversion.\n        int suffixStart = num.Length;\n        while (suffixStart > 0 && (char.IsLetter(num[suffixStart - 1]) || num[suffixStart - 1] == '%'))\n            suffixStart--;\n        var numPart = num[..suffixStart];\n        if (!double.TryParse(numPart, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var val)\n            || double.IsNaN(val) || double.IsInfinity(val))\n            throw new ArgumentException($\"Invalid {paramName} value: '{raw}'.\");\n        return val;\n    }\n\n    /// <summary>\n    /// Split an effect value string into [\"color\", \"p1\", \"p2\", …] tokens.","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/DrawingEffectsHelper.cs#L325-L361","documentation":"ParseParam reads a positional token from an effect value split into parts. If the token at the requested index exists but trims to zero length, it throws rather than silently substituting the default — so a malformed input like 'red;;5' (empty middle token) is surfaced instead of opacity/blur quietly taking the default.","triggerScenarios":"An effect string with an empty token where a parameter is expected: 'red;;5' (empty blur), 'red;5;' (empty trailing), or a whitespace-only token. parts[index] is present but num.Length == 0 after Trim.","commonSituations":"User used the wrong separator producing empty fields; trailing delimiter; copy-paste left a blank; mixing ';' and '-' separators produced an empty slot.","solutions":["Remove empty tokens — every positional parameter must have a value or be omitted by ending the string.","Use the preferred ';' separator consistently and avoid double delimiters.","If you want the default for a parameter, omit that and all following tokens rather than leaving a blank."],"exampleFix":"// before — empty middle token\neffect=\"shadow:red;;5\"\n\n// after — explicit value, or omit to take the default\neffect=\"shadow:red;4;5\"","handlingStrategy":"validation","validationCode":"static bool HasNoEmptyTokens(string value)\n{\n    var sep = value.Contains(';') ? ';' : '-';\n    foreach (var part in value.Split(sep))\n        if (string.IsNullOrWhiteSpace(part)) return false;\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try { effect = DrawingEffectsHelper.Build(value); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"(empty)\", StringComparison.Ordinal))\n{ errors.Add(ex.Message); }","preventionTips":["Never leave empty positional tokens; omit trailing parameters instead.","Use ';' consistently and avoid double delimiters.","Validate effect strings have no blank fields before parsing."],"tags":["effect","parsing","validation","user-input","drawing"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}