Humanizr/Humanizer · error · ArgumentException

Unrecognized number word: {unrecognizedWord}

Error message

Unrecognized number word: {unrecognizedWord}

What it means

This ArgumentException is thrown by GreedyCompoundWordsToNumberConverter.Convert when TryConvert fails. This converter handles locales that prefer greedy longest-token matching for glued compounds and ordinal abbreviations (e.g. '21st'). The error fires when the normalized phrase contains a token fragment that does not match any cardinal token, ordinal token, or ordinal abbreviation suffix in the locale's profile.

Source

Thrown at src/Humanizer/Localisation/WordsToNumber/GreedyCompoundWordsToNumberConverter.cs:23

/// </summary>
internal class GreedyCompoundWordsToNumberConverter(GreedyCompoundWordsToNumberProfile profile) : GenderlessWordsToNumberConverter
{
    readonly GreedyCompoundWordsToNumberProfile profile = profile;
    // Match the longest candidate first so shorter tokens do not steal the prefix of a glued
    // compound before the parser has a chance to recognize the full token.
    readonly string[] cardinalTokenOrder = profile.CardinalMap.Keys
        .OrderByDescending(static key => key.Length)
        .ToArray();
    readonly string[] ignoredTokenOrder = profile.IgnoredTokens
        .OrderByDescending(static key => key.Length)
        .ToArray();

    /// <inheritdoc />
    public override long Convert(string words)
    {
        if (!TryConvert(words, out var parsedValue, out var unrecognizedWord))
        {
            throw new ArgumentException($"Unrecognized number word: {unrecognizedWord}");
        }

        return parsedValue;
    }

    /// <inheritdoc />
    public override bool TryConvert(string words, out long parsedValue) =>
        TryConvert(words, out parsedValue, out _);

    /// <inheritdoc />
    public override bool TryConvert(string words, out long parsedValue, out string? unrecognizedWord)
    {
        if (string.IsNullOrWhiteSpace(words))
        {
            throw new ArgumentException("Input words cannot be empty.");
        }

        var trimmed = words.Trim();

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Use TryToNumber(words, out value, culture, out unrecognizedWord) for user-facing or untrusted input.
  2. Verify the locale's normalization settings (diacritic removal, character stripping) match the input's encoding.
  3. Inspect the unrecognizedWord to identify the boundary where matching failed.
  4. Constrain or pre-validate user input against the locale's known vocabulary.

Example fix

// before
long value = phrase.ToNumber(culture);

// after
if (!phrase.TryToNumber(out var value, culture, out var bad))
    Console.WriteLine($"Unrecognized: {bad}");
Defensive patterns

Strategy: try-catch

Validate before calling

bool canParse = words.TryToNumber(out var value, culture, out var unrecognized);
if (!canParse)
    Console.WriteLine($"Unrecognized: {unrecognized}");

Type guard

static bool LooksLikeNumberWords(string words, CultureInfo culture) =>
    !string.IsNullOrWhiteSpace(words) && words.TryToNumber(out _, culture, out _);

Try / catch

try
{
    return words.ToNumber(culture);
}
catch (ArgumentException ex) when (ex.Message.Contains("Unrecognized number word"))
{
    return 0;
}

Prevention

When it happens

Trigger: Calling "words".ToNumber(culture) for a greedy-compound locale with a phrase containing an unrecognized word, an ordinal abbreviation with an unsupported suffix, or glued compound fragments that do not match any known token boundary.

Common situations: Free-text user input with misspellings; ordinal abbreviations using suffixes not configured for the locale (e.g. '21st' in a locale that only supports '21.'); locale mismatch; Unicode normalization differences (diacritics) when the locale profile does not enable RemoveDiacritics; input with characters the normalization does not strip.

Related errors


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