Humanizr/Humanizer · error · ArgumentException

Empty or invalid Roman numeral string.

Error message

Empty or invalid Roman numeral string.

What it means

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.

Source

Thrown at src/Humanizer/RomanNumeralExtensions.cs:119

    /// This is a memory-efficient overload that works with character spans to avoid string allocations.
    /// Valid Roman numerals use the characters M, D, C, L, X, V, and I (case-insensitive).
    /// Supports subtractive notation (e.g., IV = 4, IX = 9).
    /// </remarks>
    /// <example>
    /// <code>
    /// "XIV".AsSpan().FromRoman() => 14
    /// "MCMXC".AsSpan().FromRoman() => 1990
    /// </code>
    /// </example>
    public static int FromRoman(CharSpan input)
    {
        input = input.Trim();

        var length = input.Length;

        if (length == 0 || IsInvalidRomanNumeral(input))
        {
            throw new ArgumentException("Empty or invalid Roman numeral string.", nameof(input));
        }

        var total = 0;
        var i = length;

        while (i > 0)
        {
            var digit = GetRomanNumeralCharValue(input[--i]);
            if (i > 0)
            {
                var previousDigit = GetRomanNumeralCharValue(input[i - 1]);
                if (previousDigit < digit)
                {
                    digit -= previousDigit;
                    i--;
                }
            }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Check that the input is non-empty and contains only valid Roman numeral characters before calling FromRoman.
  2. Use a try/catch around ArgumentException to handle invalid input gracefully.
  3. Sanitize upstream data to filter out non-Roman strings.

Example fix

// before
var value = input.AsSpan().FromRoman();

// after
static bool IsValidRoman(ReadOnlySpan<char> s) =>
    !s.Trim().IsEmpty &&
    s.Trim().IndexOfAnyExceptIn("MDCLXVImdclxvi".AsSpan()) < 0;

var value = IsValidRoman(input.AsSpan())
    ? input.AsSpan().FromRoman()
    : 0;
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidRomanNumeral(ReadOnlySpan<char> input)
{
    var trimmed = input.Trim();
    return !trimmed.IsEmpty &&
           trimmed.IndexOfAnyExceptIn("MDCLXVImdclxvi".AsSpan()) < 0;
}

Type guard

static bool IsRomanNumeral(ReadOnlySpan<char> input) =>
    IsValidRomanNumeral(input);

Try / catch

try
{
    return input.AsSpan().FromRoman();
}
catch (ArgumentException ex) when (ex.Message.Contains("Roman numeral"))
{
    return 0;
}

Prevention

When it happens

Trigger: Calling "".AsSpan().FromRoman(), " ".AsSpan().FromRoman(), or "ABC".AsSpan().FromRoman(). Also fires for numerals with invalid repetition patterns or non-Roman characters.

Common situations: 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.

Related errors


AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13). Data as JSON: /api/errors/d661233c0218d8cb. Report an issue: GitHub.