Humanizr/Humanizer · error · InvalidOperationException
Harmony-ordinal suffix data is missing.
Error message
Harmony-ordinal suffix data is missing.
What it means
The LastVowelMap suffix strategy requires a non-null suffix dictionary, but the profile supplied null. Reached from ConvertToOrdinal (profile.OrdinalSuffixes) when OrdinalSuffixStrategy is LastVowelMap, or from ConvertToTuple (profile.TupleSuffixes). Profile-data defect: the YAML set the strategy but omitted the suffix map.
Source
Thrown at src/Humanizer/Localisation/NumberToWords/HarmonyOrdinalNumberToWordsConverter.cs:185
return base.ConvertToTuple(number);
}
return AppendHarmonySuffix(Convert(number), profile.TupleSuffixes, useTerminalVowelOrdinalSuffixes: false);
}
// Vowel-harmony ordinals are the common family rule. The runtime searches backward for the
// last vowel with a configured suffix, optionally swapping to terminal-vowel suffixes only when
// the rendered ordinal stem already ends in a vowel. This keeps ordinary ordinalSuffixes as the
// default/consonant-final map for stems such as Kyrgyz "миллиард" while still allowing
// vowel-final stems such as "жыйырма" or Kazakh "екі" to use a different suffix.
/// <summary>
/// Appends the harmony suffix selected by the last vowel in the word.
/// </summary>
string AppendHarmonySuffix(string word, FrozenDictionary<char, string>? suffixes, bool useTerminalVowelOrdinalSuffixes)
{
if (suffixes is null)
{
throw new InvalidOperationException("Harmony-ordinal suffix data is missing.");
}
var terminalVowelSuffixes = profile.TerminalVowelOrdinalSuffixes;
if (useTerminalVowelOrdinalSuffixes && terminalVowelSuffixes is not null && terminalVowelSuffixes.ContainsKey(word[^1]))
{
suffixes = terminalVowelSuffixes;
}
// Walk backward so the suffix is chosen from the last vowel rather than from the first
// vowel or from a locale-specific hardcoded branch.
var suffix = string.Empty;
var suffixFoundOnLastVowel = false;
for (var i = word.Length - 1; i >= 0; i--)
{
if (suffixes.TryGetValue(word[i], out suffix))
{
suffixFoundOnLastVowel = i == word.Length - 1;View on GitHub (pinned to ffc2b77c0f)
Solutions
- Report the locale bug — the harmony suffix map is missing.
- Use a locale/version with complete harmony suffix data.
- If authoring, populate ordinalSuffixes (and tupleSuffixes if tuples use harmony) in the locale YAML.
Defensive patterns
Strategy: try-catch
Validate before calling
// Suffix maps are internal profile data; no public pre-check exists. Wrap ConvertToOrdinal/ToWords // and catch InvalidOperationException referencing the harmony suffix message.
Try / catch
string ordinal;
try
{
ordinal = number.ToOrdinalWords(culture);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Harmony-ordinal suffix data is missing"))
{
ordinal = number.ToOrdinalWords(CultureInfo.InvariantCulture);
} Prevention
- After upgrading Humanizer, run ordinal + tuple conversions for each Turkic locale.
- Report missing harmony suffix maps upstream.
- Avoid relying on tuple forms for locales you have not verified.
When it happens
Trigger: number.ToOrdinalWords(<turkic culture>) with OrdinalSuffixStrategy == LastVowelMap and profile.OrdinalSuffixes == null; or number.ToWords tuple path where profile.TupleSuffixes == null and NamedTuples misses the value.
Common situations: Locale YAML authored the strategy but left the suffix map empty; a source-generator regression that dropped the map.
Related errors
- Unsupported harmony-ordinal suffix strategy.
- Harmony-ordinal membership suffix data is incomplete.
- Unsupported conjunctional-scale strategy.
- The scale profile must include the thousand row.
- Indian scale-form number profiles require a scale value no g
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/ec4d5e8a1ebd2de0.
Report an issue: GitHub.