Humanizr/Humanizer · error · ArgumentOutOfRangeException

gender

Error message

gender

What it means

This ArgumentOutOfRangeException is the discard arm of UnitByGender on WestSlavicGenderedNumberToWordsConverter (Czech, Slovak, Polish family). The switch patterns Masculine, Feminine, Neuter, and null — covering every value the nullable GrammaticalGender? type can hold from the declared enum. The discard is reachable only through an enum carrying an undeclared integral value (invalid cast) and is otherwise unreachable.

Source

Thrown at src/Humanizer/Localisation/NumberToWords/WestSlavicGenderedNumberToWordsConverter.cs:117

    }

    /// <summary>
    /// Returns the unit form for the requested gender.
    /// </summary>
    string UnitByGender(ulong number, GrammaticalGender? gender)
    {
        if (number != 1 && number != 2)
        {
            return profile.UnitsMap[number];
        }

        return gender switch
        {
            GrammaticalGender.Masculine => profile.UnitsMasculineForms[number - 1],
            GrammaticalGender.Feminine => profile.UnitsFeminineForms[number - 1],
            GrammaticalGender.Neuter => profile.UnitsNeuterForms[number - 1],
            null => profile.UnitsInvariantForms[number - 1],
            _ => throw new ArgumentOutOfRangeException(nameof(gender))
        };
    }

    static ulong GetAbsoluteValue(long value) =>
        value >= 0 ? (ulong)value : unchecked((ulong)(-(value + 1)) + 1);
}

/// <summary>
/// Immutable generated profile for <see cref="WestSlavicGenderedNumberToWordsConverter"/>.
/// </summary>
/// <param name="minusWord">The word used to prefix negative values.</param>
/// <param name="unitsMap">The base unit lexicon.</param>
/// <param name="tensMap">The tens lexicon.</param>
/// <param name="hundredsMap">The hundreds lexicon.</param>
/// <param name="unitsMasculineForms">The masculine forms for units 1 and 2.</param>
/// <param name="unitsFeminineForms">The feminine forms for units 1 and 2.</param>
/// <param name="unitsNeuterForms">The neuter forms for units 1 and 2.</param>
/// <param name="unitsInvariantForms">The fallback forms when no gender-specific form is used.</param>

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Validate the gender with Enum.IsDefined before calling ToWords on West Slavic locales.
  2. Default invalid genders to GrammaticalGender.Masculine at the boundary where external data enters.
  3. Bind GrammaticalGender by name during (de)serialization to avoid phantom integral values.

Example fix

// before
var gender = (GrammaticalGender)dbValue;
return number.ToWords(gender, new CultureInfo("cs"));

// after
var gender = Enum.IsDefined(typeof(GrammaticalGender), dbValue)
    ? (GrammaticalGender)dbValue
    : GrammaticalGender.Masculine;
return number.ToWords(gender, new CultureInfo("cs"));
Defensive patterns

Strategy: validation

Validate before calling

bool IsValidGender(GrammaticalGender gender) =>
    gender is GrammaticalGender.Masculine
        or GrammaticalGender.Feminine
        or GrammaticalGender.Neuter;

Type guard

static bool IsDefinedGender(GrammaticalGender gender) =>
    Enum.IsDefined(typeof(GrammaticalGender), gender);

Try / catch

try
{
    return number.ToWords(gender, new CultureInfo("cs"));
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "gender")
{
    return number.ToWords(GrammaticalGender.Masculine, new CultureInfo("cs"));
}

Prevention

When it happens

Trigger: Calling Convert(number, gender) on a West Slavic locale where the gender passed to CollectLessThanThousand is an invalid GrammaticalGender cast value, causing UnitByGender to hit the discard when the number is 1 or 2.

Common situations: Passing a GrammaticalGender deserialized from an integer without validation; interop with a dynamically typed or untyped source; forwarding a raw enum code through a loosely typed API boundary.

Related errors


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