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
- 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).
- Clamp or reject such inputs in your own layer before calling ToWords.
- 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
- Centralize number-to-words calls behind a helper that bounds inputs per locale family.
- Document the 999,999,999,999 ceiling for Portuguese-family locales in your locale matrix.
- Pre-check Math.Abs against the ceiling rather than relying on the exception for control flow.
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
- The configured profile supports values from {profile.Minimum
- The configured profile supports magnitudes through {maximumM
- Unsupported harmony-ordinal suffix strategy.
- Harmony-ordinal suffix data is missing.
- Harmony-ordinal membership suffix data is incomplete.
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/85ff13fa2dd294e4.
Report an issue: GitHub.