Humanizr/Humanizer · error · InvalidOperationException

Locale '{localeCode}.{path}' defines duplicate key '{mapping

Error message

Locale '{localeCode}.{path}' defines duplicate key '{mapping.DuplicateKeys[0]}'.

What it means

In the sparse (mapping) form of minuteWordsMap, the same numeric key must not appear more than once (CanonicalLocaleAuthoring.cs:307-311). The parser records duplicate keys and the generator reports the first.

Source

Thrown at src/Humanizer.SourceGenerators/Common/CanonicalLocaleAuthoring.cs:309

            var normalizedValues = ImmutableDictionary.CreateBuilder<string, SimpleYamlValue>(StringComparer.Ordinal);
            for (var index = 0; index < sequence.Items.Length; index++)
            {
                ValidateMinuteWordValue(localeCode, path, index.ToString(CultureInfo.InvariantCulture), sequence.Items[index], allowEmpty: true);
                normalizedValues[index.ToString(CultureInfo.InvariantCulture)] = sequence.Items[index];
            }

            return new SimpleYamlMapping(normalizedValues.ToImmutable());
        }

        static SimpleYamlMapping NormalizeMinuteWordsMapping(
            string localeCode,
            string path,
            SimpleYamlMapping mapping)
        {
            if (!mapping.DuplicateKeys.IsDefaultOrEmpty)
            {
                throw new InvalidOperationException(
                    $"Locale '{localeCode}.{path}' defines duplicate key '{mapping.DuplicateKeys[0]}'.");
            }

            var normalizedValues = ImmutableDictionary.CreateBuilder<string, SimpleYamlValue>(StringComparer.Ordinal);
            foreach (var entry in mapping.Values)
            {
                if (!int.TryParse(entry.Key, NumberStyles.Integer, CultureInfo.InvariantCulture, out var minute))
                {
                    throw new InvalidOperationException(
                        $"Locale '{localeCode}.{path}' requires integer keys. Invalid key '{entry.Key}'.");
                }

                if (minute is < 0 or > 59)
                {
                    throw new InvalidOperationException(
                        $"Locale '{localeCode}.{path}' keys must be between 0 and 59. Invalid key '{entry.Key}'.");
                }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Delete the duplicate so each minute key appears once.
  2. Use canonical integer keys (no leading zeros, no quotes) to avoid string/number aliasing.
  3. Rebuild.

Example fix

# before
surfaces:
  clock:
    minuteWordsMap:
      15: "a quarter"
      15: "quarter past"
# after
surfaces:
  clock:
    minuteWordsMap:
      15: "a quarter"
Defensive patterns

Strategy: validation

Validate before calling

# SimpleYamlParser preserves duplicate keys; grep for repeats:
import re, sys
text = open(sys.argv[1], encoding='utf-8').read()
keys = re.findall(r'(?m)^\s*(\d+):', text)
seen = set()
for k in keys:
    assert k not in seen, f'duplicate minute key {k}'
    seen.add(k)

Prevention

When it happens

Trigger: Two `15:` entries, or a numeric-vs-string duplicate ("15" and 15) collapsed by the YAML parser into a duplicate.

Common situations: Merge conflicts resolved by keeping both; copy-paste of an override block.

Related errors


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