Humanizr/Humanizer · error · InvalidOperationException

Locale '{locale.LocaleCode}' has no authored or same-languag

Error message

Locale '{locale.LocaleCode}' has no authored or same-language inherited durationCases classification.

What it means

Thrown by DurationCaseCoverageInput.Create when building the coverage row for a locale that neither authors 'durationCases' in its own file (AuthoredFeatureNames doesn't contain it) nor has a variantOf parent (VariantOf is null). The coverage table needs to label each locale as authored or same-language-inherited, and a root locale with no authored durationCases and no parent has no valid label.

Source

Thrown at src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs:155

                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",
                            _ => "unsupported"
                        }
                        : locale.VariantOf is not null
                            ? "same-language-inherited"
                            : throw new InvalidOperationException(
                                $"Locale '{locale.LocaleCode}' has no authored or same-language inherited durationCases classification."),
                    locale.VariantOf));
            }

            var immutableCatalogs = catalogs.MoveToImmutable();
            var legacyCatalogs = immutableCatalogs
                .Where(static catalog => catalog.Sources.IsEmpty)
                .Select(static catalog => catalog.LocaleCode)
                .OrderBy(static locale => locale, StringComparer.Ordinal)
                .ToArray();
            if (legacyCatalogs.Length > 0)
            {
                throw new InvalidOperationException(
                    $"Release duration-case locales must use the explicit inventory, reason, sources, and provenance contract: {string.Join(", ", legacyCatalogs)}.");
            }

            var unsupportedPathBuilder = ImmutableArray.CreateBuilder<string>();
            foreach (var catalog in immutableCatalogs)

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Author the durationCases surface directly in the locale's YAML file so AuthoredFeatureNames includes 'durationCases'.
  2. If the locale is genuinely a variant, add a 'variantOf' property pointing to its parent locale.
  3. Ensure the locale catalog resolution correctly tags authored features for locales that define durationCases in their own file.

Example fix

# before (root locale with no authored durationCases and no variantOf)
locale: en
surfaces:
  list:
    engine: oxford
# after (author durationCases directly)
locale: en
surfaces:
  durationCases:
    classification: not-applicable
    reason: "English has no grammatical case for durations."
    sources: [...]
    provenance: { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure root locales either author durationCases or declare variantOf
static bool LocalesHaveClassificationSource(LocaleCatalogInput catalog) =>
    catalog.Locales.All(locale =>
        locale.AuthoredFeatureNames.Contains("durationCases") || locale.VariantOf is not null);

Prevention

When it happens

Trigger: In the coverage-row construction, the ternary checks AuthoredFeatureNames.Contains('durationCases') first, then VariantOf is not null. If both fail, the throw expression fires. This means the locale resolved durationCases from somewhere (error 46 didn't fire) but didn't author it and has no parent — an inconsistent state.

Common situations: A root/base locale (e.g. 'en') that inherits durationCases from a default/fallback mechanism rather than authoring it or declaring variantOf. This can happen if the locale resolution pipeline injects a default durationCases without marking the locale as authored or variant.

Related errors


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