Humanizr/Humanizer · error · ArgumentOutOfRangeException

gender

Error message

gender

What it means

This ArgumentOutOfRangeException is the discard arm of GetEndingForGender on UnitLeadingCompoundNumberToWordsConverter (the Luxembourgish family renderer). Unlike the terminal-ordinal converter, this switch explicitly patterns Masculine, Feminine, and Neuter — all three declared GrammaticalGender members — so the discard is only reachable via an enum value carrying an integral outside the declared range, which can only arise from an invalid cast or corrupt deserialization.

Source

Thrown at src/Humanizer/Localisation/NumberToWords/UnitLeadingCompoundNumberToWordsConverter.cs:258

    string GetCompoundUnit(int number) => profile.CompoundUnitsMap[number];

    // The tens joiner itself may need a transform such as the Eifeler rule depending on the next word.
    string GetTensJoiner(string nextWord) =>
        profile.TensJoinerTransform switch
        {
            CompoundTensJoinerTransform.None => profile.TensJoiner,
            CompoundTensJoinerTransform.Eifeler => EifelerRule.ApplyIfNeeded(profile.TensJoiner, nextWord),
            _ => throw new InvalidOperationException("Unknown unit-leading-compound tens joiner transform.")
        };

    string GetEndingForGender(GrammaticalGender gender) =>
        gender switch
        {
            GrammaticalGender.Masculine => profile.MasculineOrdinalEnding,
            GrammaticalGender.Feminine => profile.FeminineOrdinalEnding,
            GrammaticalGender.Neuter => profile.NeuterOrdinalEnding,
            _ => throw new ArgumentOutOfRangeException(nameof(gender))
        };
}

/// <summary>
/// Immutable generated profile for <see cref="UnitLeadingCompoundNumberToWordsConverter"/>.
/// </summary>
/// <param name="zeroWord">The cardinal zero word.</param>
/// <param name="minusWord">The word used to prefix negative values.</param>
/// <param name="masculineOne">The masculine form of one.</param>
/// <param name="feminineOne">The feminine form of one.</param>
/// <param name="neuterOne">The neuter form of one.</param>
/// <param name="feminineTwo">The optional feminine form of two.</param>
/// <param name="tensJoiner">The base joiner inserted between the unit and tens stem.</param>
/// <param name="tensJoinerTransform">The transform applied to <paramref name="tensJoiner"/> before emission.</param>
/// <param name="ordinalStemSuffix">The shared ordinal stem suffix appended before the gender ending.</param>
/// <param name="masculineOrdinalEnding">The masculine ordinal ending.</param>
/// <param name="feminineOrdinalEnding">The feminine ordinal ending.</param>
/// <param name="neuterOrdinalEnding">The neuter ordinal ending.</param>

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Validate the gender at the boundary: reject values outside the declared enum range before calling the converter.
  2. Map invalid values to GrammaticalGender.Masculine if graceful degradation is preferred over failing fast.
  3. Bind GrammaticalGender by name (string) during deserialization rather than by raw integer to prevent phantom values.

Example fix

// before
return number.ToOrdinalWords((GrammaticalGender)code, culture);

// after
var gender = Enum.IsDefined(typeof(GrammaticalGender), code)
    ? (GrammaticalGender)code
    : GrammaticalGender.Masculine;
return number.ToOrdinalWords(gender, culture);
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.ToOrdinalWords(gender, culture);
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "gender")
{
    return number.ToOrdinalWords(GrammaticalGender.Masculine, culture);
}

Prevention

When it happens

Trigger: Calling ConvertToOrdinal(number, gender) on a locale backed by UnitLeadingCompoundNumberToWordsConverter with a GrammaticalGender value that is not Masculine, Feminine, or Neuter (produced by an unchecked cast or unvalidated external input).

Common situations: Forwarding a gender from a loosely typed integration layer (database int, JSON number, dynamic object) into the ordinal API; unit tests using reflection-constructed enum values; deserialization frameworks that bind enums by integer.

Related errors


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