Humanizr/Humanizer · critical · InvalidOperationException

Locale inheritance cycle detected at '{localeCode}'.

Error message

Locale inheritance cycle detected at '{localeCode}'.

What it means

During locale resolution the source generator walks each locale's 'inherits' chain recursively, tracking the current path in a HashSet. If it re-enters a locale already on the resolution stack, a circular inheritance graph exists and the generator aborts with a hard build failure rather than producing infinite recursion or incorrect merged data.

Source

Thrown at src/Humanizer.SourceGenerators/Common/LocaleYamlCatalog.cs:457

        static ResolvedLocaleDefinition ResolveLocale(
            string localeCode,
            Dictionary<string, LocaleDefinition> parsedLocales,
            HashSet<string> resolving,
            ImmutableArray<ResolvedLocaleDefinition>.Builder cache,
            ImmutableArray<Diagnostic>.Builder diagnostics)
        {
            if (cache.FirstOrDefault(cached => cached.LocaleCode == localeCode) is { } cached)
                return cached;

            if (!parsedLocales.TryGetValue(localeCode, out var locale))
            {
                throw new InvalidOperationException($"Locale '{localeCode}' is not defined.");
            }

            if (!resolving.Add(localeCode))
            {
                throw new InvalidOperationException($"Locale inheritance cycle detected at '{localeCode}'.");
            }

            ResolvedLocaleDefinition inherited;
            if (string.IsNullOrWhiteSpace(locale.Inherits))
            {
                inherited = ResolvedLocaleDefinition.Empty(localeCode);
            }
            else
            {
                var inheritedLocale = locale.Inherits!;
                if (!parsedLocales.ContainsKey(inheritedLocale))
                {
                    diagnostics.Add(Diagnostic.Create(
                        HumanizerSourceGenerator.Diagnostics.InvalidLocaleDefinition,
                        Location.None,
                        localeCode,
                        $"Inherited locale '{inheritedLocale}' is not defined."));
                    inherited = ResolvedLocaleDefinition.Empty(localeCode);

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Trace the inherits chain starting from the locale code named in the message until you find the node that points back into the chain.
  2. Break the cycle by removing or correcting the 'inherits:' field on the offending locale so the graph becomes a DAG rooted at a language-only locale (e.g. 'en', 'fr', 'de').
  3. Run a quick grep of all 'inherits:' values to verify no locale is its own ancestor.

Example fix

# before — cycle:
# fr-CA.yml
inherits: fr-CH
# fr-CH.yml
inherits: fr-CA

# after — DAG:
# fr-CA.yml
inherits: fr
# fr-CH.yml
inherits: fr
Defensive patterns

Strategy: validation

Validate before calling

# Before building, verify no inherits cycle exists in all locale YAML files.
# Run as a pre-commit or CI check (PowerShell or bash + yq):
#
# Collect every locale file and build the inherits graph, then detect cycles.
import pathlib, re
locales_dir = pathlib.Path('src/Humanizer/Locales')
graph = {}
for f in locales_dir.rglob('*.yml'):
    text = f.read_text()
    code = f.stem  # or parse 'locale:' key
    m = re.search(r'^inherits:\s*(\S+)', text, re.MULTILINE)
    graph[code] = m.group(1) if m else None

def has_cycle(node, graph, seen):
    if node in seen:
        return True
    if node is None or node not in graph:
        return False
    seen.add(node)
    return has_cycle(graph[node], graph, seen)

for code in graph:
    if has_cycle(code, graph, set()):
        print(f'CYCLE detected involving {code}')

Prevention

When it happens

Trigger: Two or more locale YAML files form a cycle via their 'inherits:' field. For example 'sr-Latn.yml' declares 'inherits: sr' while 'sr.yml' declares 'inherits: sr-Latn'. A self-referential 'inherits: <own-code>' also triggers it.

Common situations: Refactoring or splitting locale files and rewiring inherits pointers; renaming a locale tag without updating dependents; accidentally setting a parent locale to inherit from a child variant.

Related errors


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