Humanizr/Humanizer · error · InvalidOperationException

Invariant bundle cannot define productive rules.

Error message

Invariant bundle cannot define productive rules.

What it means

An inflection owner with capability 'invariant' must not define any productive inflection rules. Invariant bundles represent nouns whose forms do not change across number/case, so rule-driven transformation is contradictory. The validator rejects any non-empty rules array on an invariant owner.

Source

Thrown at src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogValidation.cs:164

                throw new InvalidOperationException(
                    $"Inflection rule '{ruleId}' output must contain exactly one bounded '{{stem}}' placeholder.");
            }
        }

        static void ValidateInvariantCapability(
            string capability,
            string casing,
            ImmutableArray<InflectionLexemeInput> lexemes,
            ImmutableArray<InflectionRuleInput> rules)
        {
            if (capability != "invariant")
            {
                return;
            }

            if (!rules.IsEmpty)
            {
                throw new InvalidOperationException(
                    "Invariant bundle cannot define productive rules.");
            }

            foreach (var lexeme in lexemes)
            {
                var forms = lexeme.Singular.Accepted
                    .Concat(lexeme.DictionaryPlural.Accepted)
                    .Concat(lexeme.Display.Values.SelectMany(static form => form.Accepted))
                    .Distinct(casing == "lower-title-upper"
                        ? global::Humanizer.InflectionUnicodeData.SimpleCaseComparer.Instance
                        : StringComparer.Ordinal);
                if (forms.Skip(1).Any())
                {
                    throw new InvalidOperationException(
                        $"Invariant inflection lexeme '{lexeme.Id}' has divergent reachable forms.");
                }
            }
        }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Remove all entries from the 'rules:' list (or remove the key entirely) for the invariant owner.
  2. If the noun genuinely needs inflection, change 'capability' from 'invariant' to the appropriate productive value.
  3. Review whether the owner should be split: invariant nouns in one owner, productive nouns in another.

Example fix

# before
owners:
  - capability: 'invariant'
    rules:
      - id: 'plural-es'
        output: '{stem}s'
# after
owners:
  - capability: 'invariant'
    rules: []
Defensive patterns

Strategy: validation

Validate before calling

# Before building, verify invariant owners have no rules.
python3 -c "
import yaml, sys
for f in sys.argv[1:]:
    d = yaml.safe_load(open(f))
    owners = (d.get('inflection',{}).get('owners') or [])
    for owner in owners:
        if owner.get('capability') == 'invariant' and (owner.get('rules') or []):
            print(f'{f}: invariant owner defines rules — remove them')
" src/Humanizer/Locales/*.yml

Prevention

When it happens

Trigger: An inflection owner YAML block has 'capability: invariant' and simultaneously has a non-empty 'rules:' list. The check at line 162 fires because rules.IsEmpty is false.

Common situations: Changing a locale's capability to 'invariant' without removing its previously productive rules. Copying a productive locale as a template for an invariant one and forgetting to strip the rules section.

Related errors


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