Humanizr/Humanizer · critical · InvalidOperationException
Linked-vigesimal parser profiles require descending scales.
Error message
Linked-vigesimal parser profiles require descending scales.
What it means
This InvalidOperationException is thrown by LinkedVigesimalWordsToNumberProfile.ValidateScales (called from the profile constructor) when two adjacent scale rows are not strictly descending — i.e. when a later (smaller-position) scale has a value greater than or equal to the previous (larger) scale. The linked-vigesimal parser walks scales from largest to smallest and relies on this ordering to decompose numbers. The check fires at profile construction time, surfacing during locale registry initialization.
Solutions
- Order scale values in strictly descending order in the locale YAML (e.g. 8000, 400, 20).
- Regenerate the locale source from corrected YAML using the project's source generator.
- Verify no duplicate scale values exist in the locale's scale list.
- Add a CI test that constructs each locale profile to catch ordering violations early.
Example fix
// before (locale YAML scales) // - value: 20 // - value: 400 # out of order // after // - value: 400 // - value: 20
Defensive patterns
Strategy: validation
Validate before calling
// For locale authors: validate scale ordering before registration
static bool ScalesAreDescending(long[] scales)
{
for (var i = 1; i < scales.Length; i++)
{
if (scales[i - 1] <= scales[i])
return false;
}
return true;
} Try / catch
try
{
var converter = Configurator.GetWordsToNumberConverter(culture);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("descending scales"))
{
converter = Configurator.GetWordsToNumberConverter(fallbackCulture);
} Prevention
- Order scale values strictly descending in locale YAML (e.g. 8000, 400, 20).
- Ensure no duplicate scale values exist.
- Run the locale test suite after any YAML or generator change.
- Add CI tests that construct each locale profile to catch ordering violations.
When it happens
Trigger: Constructing a LinkedVigesimalWordsToNumberProfile with a scales array where any scale[i].Value is greater than or equal to scale[i-1].Value. This arises from locale YAML data with misordered or duplicate scale values.
Common situations: Editing locale YAML and accidentally swapping scale rows or duplicating a value; a locale generator sorting scales incorrectly; forking a locale and reordering entries; a merge conflict that scrambled scale ordering.
Related errors
- Linked-vigesimal parser profiles require positive scale…
- Linked-vigesimal parser profiles require descending scales…
- A dedicated 71 spelling is required when…
- Cannot extract negative prefix for culture
- A property path is required.
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/faa895f5cd9f1a8f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Humanizer/Localisation/WordsToNumber/LinkedVigesimalWordsToNumberConverter.cs:498
static string[] Tokenize(string value) =>
value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
static string Normalize(string value) =>
string.Join(" ", value.Trim().ToLowerInvariant().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries));
static LinkedVigesimalScale[] ValidateScales(LinkedVigesimalScale[] value)
{
for (var i = 0; i < value.Length; i++)
{
if (value[i].Value <= 0)
{
throw new InvalidOperationException("Linked-vigesimal parser profiles require positive scale values.");
}
if (i > 0 && value[i - 1].Value <= value[i].Value)
{
throw new InvalidOperationException("Linked-vigesimal parser profiles require descending scales.");
}
}
return value;
}
}
/// <summary>Pre-tokenized linked-vigesimal scale phrase.</summary>
readonly record struct TokenizedLinkedVigesimalScale(long Value, string[] OneTokens, string[] OneWithRemainderTokens, string[] NameTokens, string[] NameWithRemainderTokens);View on GitHub (pinned to ffc2b77c0f)