Humanizr/Humanizer · error · ArgumentOutOfRangeException

Legacy billion profiles support values through 999,999,999,9

Error message

Legacy billion profiles support values through 999,999,999,999.

What it means

Portuguese-family locales (e.g. pt, pt-BR) whose profile lacks authored large-scale words cap cardinal conversion at 999,999,999,999 via the 'legacy billion' path. Any absolute value above that exceeds what the legacy profile can decompose and throws ArgumentOutOfRangeException. The cap lifts only when the profile declares Cardinal.HasAuthoredScales, which switches in the full scale table.

Source

Thrown at src/Humanizer/Localisation/NumberToWords/BillionStrategyNumberToWordsConverter.cs:35

{
    /// <summary>
    /// Immutable generated profile that owns the cardinal and ordinal billion-scale lexicons.
    /// </summary>
    readonly BillionStrategyNumberToWordsProfile profile = profile;

    /// <summary>
    /// Converts the given value using the locale's billion-scale cardinal rules.
    /// </summary>
    /// <param name="input">The number to convert.</param>
    /// <param name="gender">The grammatical gender to use when the locale distinguishes it.</param>
    /// <param name="addAnd">Reserved for compatibility with other converters; this implementation derives conjunction placement from the generated profile.</param>
    /// <returns>The localized cardinal words for <paramref name="input"/>.</returns>
    public override string Convert(long input, GrammaticalGender gender, bool addAnd = true)
    {
        var magnitude = GetAbsoluteValue(input);
        if (!profile.Cardinal.HasAuthoredScales && magnitude > 999_999_999_999)
        {
            throw new ArgumentOutOfRangeException(nameof(input), input, "Legacy billion profiles support values through 999,999,999,999.");
        }

        if (!profile.Cardinal.HasAuthoredScales && magnitude >= 1_000_000_000)
        {
            switch (profile.Cardinal.BillionStrategy)
            {
                case BillionCardinalStrategy.ThousandMillions:
                    break;
                case BillionCardinalStrategy.BillionWord when magnitude / 1_000_000_000 == 1 && profile.Cardinal.BillionSingularWord is null:
                    throw new InvalidOperationException("Billion-word cardinal strategy requires a singular billion word.");
                case BillionCardinalStrategy.BillionWord when magnitude / 1_000_000_000 > 1 && profile.Cardinal.BillionPluralWord is null:
                    throw new InvalidOperationException("Billion-word cardinal strategy requires a plural billion word.");
                case BillionCardinalStrategy.BillionWord:
                    break;
                default:
                    throw new InvalidOperationException("Unsupported billion-strategy cardinal mode.");
            }
        }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Pre-check Math.Abs(value) > 999_999_999_999L for Portuguese-family cultures and route those values to a culture with authored scales (e.g. invariant/English).
  2. Clamp or reject such inputs in your own layer before calling ToWords.
  3. Contribute authored large scales (HasAuthoredScales) to the locale profile to lift the ceiling.

Example fix

// before
var w = value.ToWords(new CultureInfo("pt-BR"));

// after — guard the legacy billion ceiling
var ci = new CultureInfo("pt-BR");
var ceiling = 999_999_999_999L;
var w = value == long.MinValue || (ulong)(value < 0 ? -value : value) > (ulong)ceiling
    ? value.ToWords(CultureInfo.InvariantCulture)
    : value.ToWords(ci);
Defensive patterns

Strategy: validation

Validate before calling

static readonly long LegacyBillionCeiling = 999_999_999_999L;

static bool IsWithinLegacyBillionRange(long value) =>
    value != long.MinValue && Math.Abs(value) <= LegacyBillionCeiling;

// usage for Portuguese-family cultures:
var ci = new CultureInfo("pt-BR");
if (!IsWithinLegacyBillionRange(value))
{
    // route to a culture with authored scales instead
    return value.ToWords(CultureInfo.InvariantCulture);
}

Prevention

When it happens

Trigger: 1_000_000_000_000L.ToWords(new CultureInfo("pt-BR")) — any long whose absolute value exceeds 999,999,999,999 when profile.Cardinal.HasAuthoredScales is false. Fires before any decomposition begins.

Common situations: Passing huge numeric IDs, byte counts, or financial totals to ToWords under a Portuguese culture; assuming all locales handle long.MaxValue.

Related errors


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