Humanizr/Humanizer · error · InvalidOperationException

Duplicate inflection rule id '{id}'.

Error message

Duplicate inflection rule id '{id}'.

What it means

Rule ids must be unique within a locale (ordinal comparison). ParseRules tracks ids in a HashSet and throws when a second entry reuses an id.

Source

Thrown at src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogParsing.cs:434

            var rules = ImmutableArray.CreateBuilder<InflectionRuleInput>();
            var ids = new HashSet<string>(StringComparer.Ordinal);
            foreach (var item in sequence.Items)
            {
                if (item is not SimpleYamlMapping rule)
                {
                    throw new InvalidOperationException("Inflection rules must be mappings.");
                }

                ValidateProperties(
                    rule,
                    "Inflection rule",
                    "id", "direction", "priority", "scope", "match", "output",
                    "hostileExclusions", "hostile-exclusions", "reverse", "sources");
                var id = GetRequiredScalar(rule, "id", "Inflection rule must define id");
                if (!ids.Add(id))
                {
                    throw new InvalidOperationException($"Duplicate inflection rule id '{id}'.");
                }

                ValidateSources(rule, sourceIds, $"Inflection rule '{id}'");
                var direction = GetRequiredScalar(rule, "direction", $"Inflection rule '{id}' must define direction");
                if (direction is not ("forward" or "reverse"))
                {
                    throw new InvalidOperationException($"Inflection rule '{id}' has unsupported direction '{direction}'.");
                }

                var priority = GetRequiredInt(rule, "priority", $"Inflection rule '{id}' must define priority");
                var scope = GetRequiredMapping(rule, "scope", $"Inflection rule '{id}' must define scope");
                ValidateProperties(
                    scope,
                    $"Inflection rule '{id}' scope",
                    "pos", "countability", "token", "scripts");
                if (GetRequiredScalar(scope, "pos", $"Inflection rule '{id}' scope must define pos") != "noun" ||
                    GetRequiredScalar(scope, "token", $"Inflection rule '{id}' scope must define token") != "standalone")
                {

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Give each rule a unique id (e.g. encode direction and suffix in the id, like 'zz.forward.consonant-y')

Example fix

# before
      - id: 'zz.forward.consonant-y'
        ...
      - id: 'zz.forward.consonant-y'
# after
      - id: 'zz.forward.consonant-y'
        ...
      - id: 'zz.forward.vowel-y'
Defensive patterns

Strategy: validation

Validate before calling

var ids = new HashSet<string>(StringComparer.Ordinal);
foreach (var rule in rules)
    if (!ids.Add(rule.Id))
        Report($"{locale}: duplicate rule id '{rule.Id}'.");

Type guard

static bool RuleIdsUnique(IEnumerable<Rule> rules) =>
    rules.Select(r => r.Id).Distinct(StringComparer.Ordinal).Count() == rules.Count();

Prevention

When it happens

Trigger: Two 'rules:' entries share the same 'id:' value.

Common situations: Copy-pasting a rule block to create a variant without renaming the id.

Related errors


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