{"record":{"id":"d661233c0218d8cb","repo":"Humanizr/Humanizer","slug":"empty-or-invalid-roman-numeral-string","errorCode":null,"errorMessage":"Empty or invalid Roman numeral string.","messagePattern":"Empty or invalid Roman numeral string\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Humanizer/RomanNumeralExtensions.cs","lineNumber":119,"sourceCode":"    /// This is a memory-efficient overload that works with character spans to avoid string allocations.\n    /// Valid Roman numerals use the characters M, D, C, L, X, V, and I (case-insensitive).\n    /// Supports subtractive notation (e.g., IV = 4, IX = 9).\n    /// </remarks>\n    /// <example>\n    /// <code>\n    /// \"XIV\".AsSpan().FromRoman() => 14\n    /// \"MCMXC\".AsSpan().FromRoman() => 1990\n    /// </code>\n    /// </example>\n    public static int FromRoman(CharSpan input)\n    {\n        input = input.Trim();\n\n        var length = input.Length;\n\n        if (length == 0 || IsInvalidRomanNumeral(input))\n        {\n            throw new ArgumentException(\"Empty or invalid Roman numeral string.\", nameof(input));\n        }\n\n        var total = 0;\n        var i = length;\n\n        while (i > 0)\n        {\n            var digit = GetRomanNumeralCharValue(input[--i]);\n            if (i > 0)\n            {\n                var previousDigit = GetRomanNumeralCharValue(input[i - 1]);\n                if (previousDigit < digit)\n                {\n                    digit -= previousDigit;\n                    i--;\n                }\n            }\n","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer/RomanNumeralExtensions.cs#L101-L137","documentation":"Thrown by FromRoman when the input span is empty after trimming or contains characters that do not form a valid Roman numeral. Valid Roman numerals use only M, D, C, L, X, V, and I (case-insensitive) with subtractive notation. Humanizer validates the character set before computing the integer value.","triggerScenarios":"Calling \"\".AsSpan().FromRoman(), \"  \".AsSpan().FromRoman(), or \"ABC\".AsSpan().FromRoman(). Also fires for numerals with invalid repetition patterns or non-Roman characters.","commonSituations":"Parsing user input or data-file fields that may contain non-Roman text. Reading from a column that is sometimes blank. Passing lowercased or Unicode-variant Roman characters that fail validation.","solutions":["Check that the input is non-empty and contains only valid Roman numeral characters before calling FromRoman.","Use a try/catch around ArgumentException to handle invalid input gracefully.","Sanitize upstream data to filter out non-Roman strings."],"exampleFix":"// before\nvar value = input.AsSpan().FromRoman();\n\n// after\nstatic bool IsValidRoman(ReadOnlySpan<char> s) =>\n    !s.Trim().IsEmpty &&\n    s.Trim().IndexOfAnyExceptIn(\"MDCLXVImdclxvi\".AsSpan()) < 0;\n\nvar value = IsValidRoman(input.AsSpan())\n    ? input.AsSpan().FromRoman()\n    : 0;","handlingStrategy":"validation","validationCode":"static bool IsValidRomanNumeral(ReadOnlySpan<char> input)\n{\n    var trimmed = input.Trim();\n    return !trimmed.IsEmpty &&\n           trimmed.IndexOfAnyExceptIn(\"MDCLXVImdclxvi\".AsSpan()) < 0;\n}","typeGuard":"static bool IsRomanNumeral(ReadOnlySpan<char> input) =>\n    IsValidRomanNumeral(input);","tryCatchPattern":"try\n{\n    return input.AsSpan().FromRoman();\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"Roman numeral\"))\n{\n    return 0;\n}","preventionTips":["Filter input to valid Roman characters before calling FromRoman.","Use a regex like ^[MDCLXVI]+$ (case-insensitive) for quick validation.","Handle empty or null spans before the call."],"tags":["roman-numeral","argument-validation","input-parsing"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}