Humanizr/Humanizer · critical · InvalidOperationException

Linked-vigesimal parser profiles require positive scale valu

Error message

Linked-vigesimal parser profiles require positive scale values.

What it means

This InvalidOperationException is thrown by LinkedVigesimalWordsToNumberProfile.ValidateScales (called from the profile constructor) when any scale row has a Value of zero or less. Scale values must be positive integers because they serve as multipliers and divisors in the parser. This is a locale-authoring invariant caught at profile construction time, meaning it fires during locale registry initialization — typically at application start or first use of the affected culture.

Source

Thrown at src/Humanizer/Localisation/WordsToNumber/LinkedVigesimalWordsToNumberConverter.cs:493

        new[] { terminalRemainderJoiner, terminalRemainderAlternateJoiner }
            .Select(Tokenize)
            .Where(static tokens => tokens.Length > 0)
            .OrderByDescending(static tokens => tokens.Length)
            .ToArray();

    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)

Solutions

  1. Ensure every scale row in the locale YAML has a positive integer value.
  2. Regenerate the locale source from corrected YAML using the project's source generator.
  3. Audit the locale's scale definitions against the numeral system's expected progression.
  4. Add a CI test that constructs each locale profile to catch invalid scale values early.

Example fix

// before (locale YAML fragment)
//  - value: 0

// after
//  - value: 20
Defensive patterns

Strategy: validation

Validate before calling

// For locale authors: validate all scale values are positive
static bool ScalesArePositive(long[] scales) =>
    scales.All(static s => s > 0);

Try / catch

try
{
    // Profile construction happens during locale registry init
    var converter = Configurator.GetWordsToNumberConverter(culture);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("positive scale values"))
{
    // Locale data is corrupt; use a fallback locale
    converter = Configurator.GetWordsToNumberConverter(fallbackCulture);
}

Prevention

When it happens

Trigger: Constructing a LinkedVigesimalWordsToNumberProfile with a scales array containing a row whose Value is 0 or negative. This arises from locale YAML data that has a missing, miscalculated, or corrupted scale value entry.

Common situations: Hand-editing locale YAML and accidentally leaving a scale value blank or zero; a locale generator emitting a default or placeholder value; corrupting a locale file during a merge or fork; a regression in the source generator's scale computation.

Related errors


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