Humanizr/Humanizer · error · InvalidOperationException
Cannot extract negative prefix for culture '{culture.Name}'
Error message
Cannot extract negative prefix for culture '{culture.Name}' from number-word-suffix ordinalizer. What it means
This InvalidOperationException is thrown by NumberWordSuffixOrdinalizer.ConvertCore when ordinalizing a negative number whose magnitude has an exact replacement. The ordinalizer renders the magnitude as a cardinal word, renders the full negative number as a cardinal word, then verifies that the negative phrase ends with the magnitude phrase so it can strip it and prepend the negative prefix. If the locale's number-to-words converter produces a negative phrase that does not end with the magnitude cardinal (e.g. the negative word is infix or the word order differs), the extraction fails and this error signals a locale-data inconsistency.
Source
Thrown at src/Humanizer/Localisation/Ordinalizers/NumberWordSuffixOrdinalizer.cs:59
var block = ResolveGenderBlock(effectiveGender);
// Negative numbers: check the absolute magnitude against exact replacements.
// When found, compose the negative ordinal using the locale's negative prefix
// (extracted from the converter) plus the ordinalizer's own exact replacement.
// This ensures negative and positive irregulars both come from ExactReplacements.
if (number < 0)
{
var magnitude = GetAbsoluteValue(number);
if (magnitude <= int.MaxValue
&& block.ExactReplacements.TryGetValue((int)magnitude, out var negExact))
{
var converter = Configurator.GetNumberToWordsConverter(culture);
var magnitudeCardinal = converter.Convert((long)magnitude, effectiveGender);
var negativeCardinal = converter.Convert(number, effectiveGender);
if (!negativeCardinal.EndsWith(magnitudeCardinal, StringComparison.Ordinal))
{
throw new InvalidOperationException(
$"Cannot extract negative prefix for culture '{culture.Name}' from number-word-suffix ordinalizer.");
}
return negativeCardinal[..^magnitudeCardinal.Length] + negExact;
}
var cardinal = Configurator.GetNumberToWordsConverter(culture).Convert(number, effectiveGender);
return cardinal + block.DefaultSuffix;
}
if (TryGetInt32Value(number, out var exactValue) &&
block.ExactReplacements.TryGetValue(exactValue, out var exact))
{
return exact;
}
var positiveCardinal = Configurator.GetNumberToWordsConverter(culture).Convert(number, effectiveGender);
return positiveCardinal + block.DefaultSuffix;View on GitHub (pinned to ffc2b77c0f)
Solutions
- Verify the locale's number-to-words converter renders negatives as a prefix + the magnitude cardinal (e.g. 'minus' + 'three'), so the suffix extraction succeeds.
- If the locale uses an infix or circumfixed negative form, remove the problematic negative magnitude from the ordinalizer's ExactReplacements for that gender block.
- Run a round-trip test: converter.Convert(-N) must EndsWith converter.Convert(N) for each exact-replacement magnitude.
- Regenerate the locale source from corrected YAML using the project's source generator.
Example fix
// before: locale converter renders negatives as infix, // so Convert(-3) => "tri minus" does not end with Convert(3) => "tri" // after: configure the locale's minus word as a prefix // so Convert(-3) => "minus tri" which ends with "tri" // enabling the ordinalizer to extract "minus " + "tretji"
Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the locale's cardinal converter produces prefix-style negatives
// before relying on the ordinalizer for negative exact replacements
var culture = new CultureInfo("xx");
var converter = Configurator.GetNumberToWordsConverter(culture);
var positive = converter.Convert(3L, GrammaticalGender.Masculine);
var negative = converter.Convert(-3L, GrammaticalGender.Masculine);
bool isSafeForOrdinalizer = negative.EndsWith(positive, StringComparison.Ordinal); Try / catch
try
{
return number.ToOrdinalWords(gender, culture);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("negative prefix"))
{
// Locale data inconsistency: fall back to a culture with known-good negative rendering
return number.ToOrdinalWords(gender, fallbackCulture);
} Prevention
- Write a round-trip test for each locale: converter.Convert(-N) must EndsWith converter.Convert(N) for exact-replacement magnitudes.
- Review locale YAML changes that alter the negative prefix strategy before shipping.
- Keep the ordinalizer's ExactReplacements consistent with the cardinal converter's negative rendering.
- Run the full locale test suite after any change to negative handling.
When it happens
Trigger: Calling an ordinalize API on a negative number for a locale whose NumberWordSuffixOrdinalizer has exact replacements that include the magnitude, when the underlying cardinal converter for that culture renders negatives in a way that does not place the magnitude cardinal as a trailing substring of the full negative phrase.
Common situations: Adding or modifying a locale's negative-prefix strategy in YAML so that it no longer prefixes the magnitude; introducing a new exact ordinal replacement for a negative magnitude without verifying the cardinal converter's negative form; upgrading Humanizer where a locale's negative rendering changed between versions; locale data regression where the minus word becomes infix.
Related errors
- A dedicated 71 spelling is required when specialSeventyOneEn
- Linked-vigesimal parser profiles require descending scales w
- Linked-vigesimal parser profiles require positive scale valu
- Linked-vigesimal parser profiles require descending scales.
- '{path}.units' must explicitly define '{unitName}'.
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/bbec2061e4b8d0c8.
Report an issue: GitHub.