Humanizr/Humanizer · error · InvalidOperationException
Locale '{locale.LocaleCode}' must classify the durationCases
Error message
Locale '{locale.LocaleCode}' must classify the durationCases surface. What it means
Thrown by DurationCaseCoverageInput.Create when iterating release locales and a locale's ResolvedFeatures dictionary has no 'durationCases' entry. Every locale shipped in a release must classify how duration units inflect by grammatical case — the generator cannot produce coverage tables without this classification.
Source
Thrown at src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs:132
public static DurationCaseCoverageInput Create(LocaleCatalogInput localeCatalog)
{
if (!localeCatalog.Diagnostics.IsEmpty)
{
throw new InvalidOperationException(
string.Join(
"\n",
localeCatalog.Diagnostics
.Select(static diagnostic => diagnostic.GetMessage())
.OrderBy(static message => message, StringComparer.Ordinal)));
}
var catalogs = ImmutableArray.CreateBuilder<DurationCaseCatalog>(localeCatalog.Locales.Length);
var rows = ImmutableArray.CreateBuilder<DurationCaseCoverageRow>(localeCatalog.Locales.Length);
foreach (var locale in localeCatalog.Locales)
{
if (!locale.ResolvedFeatures.TryGetValue("durationCases", out var value))
{
throw new InvalidOperationException(
$"Locale '{locale.LocaleCode}' must classify the durationCases surface.");
}
locale.ResolvedFeatures.TryGetValue("phrases", out var phrases);
var catalog = DurationCaseNormalization.Parse(
locale.LocaleCode,
value,
GetExplicitDurationCaseNumberDetector(locale),
phrases);
catalogs.Add(catalog);
rows.Add(new DurationCaseCoverageRow(
locale.LocaleCode,
locale.AuthoredFeatureNames.Contains("durationCases")
? catalog.Classification switch
{
DurationCaseClassification.Distinct => "distinct",
DurationCaseClassification.Invariant => "invariant",
DurationCaseClassification.NotApplicable => "not-applicable",View on GitHub (pinned to ffc2b77c0f)
Solutions
- Add a durationCases surface to the locale file with a classification (e.g. 'durationCases:\n classification: not-applicable' with reason/sources/provenance if it's a release locale).
- If the locale is a variant, ensure its variantOf parent has a durationCases classification that resolves down.
- For locales where duration cases don't apply, classify as 'not-applicable' with the required reason and provenance contract.
Example fix
# before (no durationCases surface)
surfaces:
list:
engine: oxford
# after
surfaces:
list:
engine: oxford
durationCases:
classification: not-applicable
reason: "English does not inflect duration units by case."
sources: []
provenance: { ... } Defensive patterns
Strategy: validation
Validate before calling
// Ensure every release locale resolves a durationCases feature
static bool AllLocalesHaveDurationCases(LocaleCatalogInput catalog) =>
catalog.Locales.All(locale => locale.ResolvedFeatures.ContainsKey("durationCases")); Prevention
- Every release locale must have a durationCases surface or inherit one via variantOf.
- Add durationCases to new locale files from the start.
- Use 'classification: not-applicable' with reason/sources/provenance for languages without grammatical case.
When it happens
Trigger: Create loops over localeCatalog.Locales and calls TryGetValue('durationCases'). If it returns false for any locale, this throws. This happens when a locale file omits the durationCases surface entirely and no parent locale provides it via inheritance resolution.
Common situations: A new locale is added without a durationCases surface block and without a variantOf parent that resolves one. Or an existing locale's durationCases block was accidentally deleted during editing. Release builds enforce completeness, so dev/preview locales that skip this will fail the release gate.
Related errors
- Release duration-case completeness requires zero unsupported
- Locale '{locale.LocaleCode}' has no authored or same-languag
- Release duration-case locales must use the explicit inventor
- '{path}' must use the explicit inventory, reason, sources, a
- '{path}' must define exactly one of phrase, sameRenderedAs,
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/195d05e5ea474e45.
Report an issue: GitHub.