Humanizr/Humanizer · error · InvalidOperationException

Billion-word cardinal strategy requires a singular billion w

Error message

Billion-word cardinal strategy requires a singular billion word.

What it means

Thrown when a locale's cardinal scale declares billionStrategy: 'billion-word' but omits the billionSingularWord property. The generator builds a BillionStrategyCardinalScale[] that needs an explicit singular billion term; without it the generated converter would lack a word for 1,000,000,000. It is a schema-completeness check keyed off the strategy value.

Source

Thrown at src/Humanizer.SourceGenerators/Common/GenerationHelpers.cs:736

            builder.Append(')');
            first = false;
        }

        builder.Append(" }");
        return builder.ToString();
    }

    static string CreateLegacyBillionStrategyCardinalScaleArrayExpression(JsonElement cardinalElement)
    {
        var millionScale = $"new(1000000UL, {QuoteLiteral(GetRequiredString(cardinalElement, "millionSingularWord"))}, {QuoteLiteral(GetRequiredString(cardinalElement, "millionPluralWord"))})";
        if (GetRequiredString(cardinalElement, "billionStrategy") != "billion-word")
        {
            return $"new BillionStrategyCardinalScale[] {{ {millionScale} }}";
        }

        var billionSingular = GetOptionalString(cardinalElement, "billionSingularWord")
            ?? throw new InvalidOperationException("Billion-word cardinal strategy requires a singular billion word.");
        var billionPlural = GetOptionalString(cardinalElement, "billionPluralWord")
            ?? throw new InvalidOperationException("Billion-word cardinal strategy requires a plural billion word.");
        return $"new BillionStrategyCardinalScale[] {{ new(1000000000UL, {QuoteLiteral(billionSingular)}, {QuoteLiteral(billionPlural)}), {millionScale} }}";
    }

    static string CreateContractedOneScaleArrayExpression(JsonElement arrayElement)
    {
        if (arrayElement.ValueKind != JsonValueKind.Array)
        {
            throw new InvalidOperationException("Expected JSON array.");
        }

        var builder = new StringBuilder("new ContractedOneScaleNumberToWordsConverter.Scale[] { ");
        var first = true;

        foreach (var item in arrayElement.EnumerateArray())
        {
            if (item.ValueKind != JsonValueKind.Object)

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. In the offending locale YAML under numberToWords cardinal, add a 'billionSingularWord' key with the singular billion term (e.g. billionSingularWord: 'un milliard').
  2. If the locale uses the thousand-millions scale instead, set billionStrategy to 'thousand-millions' so no billion word is required.
  3. Cross-check against a known billion-word locale (en.yml, fr.yml) for the exact key names and placement.

Example fix

# before
cardinal:
  billionStrategy: 'billion-word'
  millionSingularWord: 'milhão'
# after
cardinal:
  billionStrategy: 'billion-word'
  billionSingularWord: 'mil milhões'
  billionPluralWord: 'mil milhões'
  millionSingularWord: 'milhão'
Defensive patterns

Strategy: validation

Validate before calling

# In the locale YAML, before setting billionStrategy: 'billion-word',
# confirm both keys exist. Quick check with ripgrep:
#   rg -A2 "billionStrategy: 'billion-word'" src/Humanizer/Locales/<code>.yml
# Ensure 'billionSingularWord' appears in the same cardinal block.

Prevention

When it happens

Trigger: A locale YAML sets numberToWords cardinal.billionStrategy to 'billion-word' (vs 'thousand-millions' or 'thousandth-millionth') but does not supply a parallel billionSingularWord string. The check runs in CreateLegacyBillionStrategyCardinalScaleArrayExpression during code generation.

Common situations: A contributor copies an existing billion-word locale (e.g. en, fr) and deletes or renames the billionSingularWord key, or switches billionStrategy from another value to 'billion-word' without adding the singular/plural word pair. Surfaces as a generator failure during dotnet build.

Related errors


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