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
- Delete the duplicate so each minute key appears once.
- Use canonical integer keys (no leading zeros, no quotes) to avoid string/number aliasing.
- 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
- Author locales against the canonical schema; the error message lists the exact allowed names.
- Run `dotnet build src/Humanizer/Humanizer.csproj` locally so the generator reports locale errors before push.
- Copy an existing compliant locale file as your template rather than writing YAML from scratch.
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
- Locale '{localeCode}.surfaces.clock.minuteWordsMap' defines
- Locale '{localeCode}.{path}' must be a sequence or sparse nu
- Locale '{localeCode}.{path}' must contain at most 60 entries
- Locale '{localeCode}.{path}' requires integer keys. Invalid
- Locale '{localeCode}.{path}' keys must be between 0 and 59.
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/e9f24dc805b21da2.
Report an issue: GitHub.