Humanizr/Humanizer · error · InvalidOperationException
Inflection {subject} has an unsupported casing expansion.
Error message
Inflection {subject} has an unsupported casing expansion. What it means
For casing mode 'lower-title-upper', the validator calls TryNormalizeSimpleLower which walks each Unicode scalar and checks that the simple lowercasing does not change the string length (scalar width). If any scalar's simple-case mapping produces a different number of UTF-16 code units than the original, the casing expansion is rejected because it would break the stem-substitution invariant.
Source
Thrown at src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogValidation.cs:240
{
string normalized;
try
{
normalized = value.IsNormalized(NormalizationForm.FormC)
? value
: value.Normalize(NormalizationForm.FormC);
}
catch (ArgumentException)
{
throw new InvalidOperationException(
$"Inflection {subject} contains invalid Unicode.");
}
if (casing == "lower-title-upper")
{
if (!TryNormalizeSimpleLower(normalized, out var lower))
{
throw new InvalidOperationException(
$"Inflection {subject} has an unsupported casing expansion.");
}
normalized = lower;
}
var literal = allowStemPlaceholder
? normalized.Replace("{stem}", string.Empty)
: normalized;
if (literal.Length > 0 &&
!HasOnlyDeclaredScripts(literal, ownerScripts, allowNonLetters))
{
throw new InvalidOperationException(
$"Inflection {subject} contains text outside its declared scripts.");
}
return normalized;
}View on GitHub (pinned to ffc2b77c0f)
Solutions
- Replace the problematic character with one whose simple lowercasing preserves scalar width, or use a casing mode other than 'lower-title-upper'.
- If the character is essential, check whether InflectionUnicodeData pins it as a letter/mark with a width-preserving mapping, or file an issue to extend the pinned set.
- Split the lexeme so the problematic form lives in a separate casing context.
Example fix
# before — eszett expands when lowercased in some mappings accepted: - 'STRAße' # after — use case-stable form or switch casing mode accepted: - 'strasse'
Defensive patterns
Strategy: validation
Validate before calling
# Before building, check for characters whose simple lowercasing changes scalar width.
python3 -c "
import unicodedata, sys
# Flag known problematic characters for 'lower-title-upper' casing mode
suspects = {'\u00DF':'LATIN SMALL LETTER SHARP S','\u0130':'LATIN CAPITAL LETTER I WITH DOT ABOVE'}
for ch, name in suspects.items():
print(f'Caution: U+{ord(ch):04X} {name} may break width-preserving casing')
" src/Humanizer/Locales/*.yml Prevention
- For 'lower-title-upper' casing mode, avoid characters like sharp-s (U+00DF) and capital I-with-dot (U+0130) whose case mappings change string length.
- Use the precomposed or expanded form that preserves width across casing.
- Test new locale text against TryNormalizeSimpleLower logic before committing.
When it happens
Trigger: An authored text contains a character whose Unicode simple-case mapping expands or contracts (e.g., a character that lowercases to a multi-code-unit sequence). TryNormalizeSimpleLower returns false when (lower scalar width) != (original scalar width) or (upper scalar width) != (original scalar width).
Common situations: Using characters with special casing behavior like the German eszett (ß), certain Greek sigma forms, or characters outside the BMP whose case mappings cross the surrogate boundary. Locale data that includes archaic or unusual scripts with non-width-preserving case mappings.
Related errors
- Inflection {subject} contains a duplicate after normalizatio
- Inflection {subject} contains invalid Unicode.
- Inflection {subject} contains text outside its declared scri
- Productive evidence '{direction}' requires at least 100 atte
- {subject} references unknown source '{source}'.
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/673df6fa7f1e1004.
Report an issue: GitHub.