Humanizr/Humanizer · error · InvalidOperationException

Unsupported harmony-ordinal suffix strategy.

Error message

Unsupported harmony-ordinal suffix strategy.

What it means

ConvertToOrdinal's switch over profile.OrdinalSuffixStrategy hit its default arm — the profile declared a HarmonyOrdinalSuffixStrategy value the converter does not handle. This is a profile-data / source-generator defect, not user input. It fires during ToOrdinalWords() for a Turkic locale whose ordinal strategy is out of range.

Source

Thrown at src/Humanizer/Localisation/NumberToWords/HarmonyOrdinalNumberToWordsConverter.cs:148

        value >= 0 ? (ulong)value : unchecked((ulong)(-(value + 1)) + 1);

    /// <summary>
    /// Converts the given value using the locale's harmony-based ordinal rules.
    /// </summary>
    /// <param name="number">The number to convert.</param>
    /// <returns>The localized ordinal words for <paramref name="number"/>.</returns>
    public override string ConvertToOrdinal(int number)
    {
        // Ordinals are the cardinal form plus a suffix strategy chosen by the generated profile.
        var word = Convert(number);
        return profile.OrdinalSuffixStrategy switch
        {
            HarmonyOrdinalSuffixStrategy.LastVowelMap => AppendHarmonySuffix(word, profile.OrdinalSuffixes, useTerminalVowelOrdinalSuffixes: true),
            HarmonyOrdinalSuffixStrategy.FinalCharacterMembership => AppendMembershipOrdinalSuffix(
                word,
                profile.SecondOrdinalSuffixCharacters,
                profile.OrdinalSuffixPair),
            _ => throw new InvalidOperationException("Unsupported harmony-ordinal suffix strategy.")
        };
    }

    /// <summary>
    /// Converts the given value to a tuple form, using either a named tuple or harmony suffixes.
    /// </summary>
    /// <param name="number">The number to convert.</param>
    /// <returns>The localized tuple words for <paramref name="number"/>.</returns>
    public override string ConvertToTuple(int number)
    {
        // Exact tuple names win first; only unknown tuples fall back to harmony-suffix rendering.
        if (profile.NamedTuples is not null && profile.NamedTuples.TryGetValue(number, out var namedTuple))
        {
            return namedTuple;
        }

        if (profile.TupleSuffixes is null)
        {

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Report the locale + version as a library/locale-data bug.
  2. Use a Humanizer version where profile data and the switch agree.
  3. If authoring a locale, set OrdinalSuffixStrategy to LastVowelMap or FinalCharacterMembership.
Defensive patterns

Strategy: try-catch

Validate before calling

// OrdinalSuffixStrategy is internal; callers cannot pre-validate. Catch InvalidOperationException
// from ConvertToOrdinal and fall back to a known-good locale.

Try / catch

string ordinal;
try
{
    ordinal = number.ToOrdinalWords(culture);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("harmony-ordinal suffix strategy"))
{
    ordinal = number.ToOrdinalWords(CultureInfo.InvariantCulture);
}

Prevention

When it happens

Trigger: number.ToOrdinalWords(new CultureInfo(<turkic culture>)) where profile.OrdinalSuffixStrategy is neither LastVowelMap nor FinalCharacterMembership.

Common situations: Version mismatch where a new enum member was added without a corresponding switch case; hand-authored locale data with an invalid strategy value.

Related errors


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