Humanizr/Humanizer · critical · InvalidOperationException

Locale '{localeCode}.{path}' defines duplicate numeric minut

Error message

Locale '{localeCode}.{path}' defines duplicate numeric minute slot {minute}.

What it means

Thrown by the Humanizer source generator while normalizing the clock 'minutes' mapping of a locale YAML file. After keys are parsed to integers and canonicalized, two distinct string keys (e.g. '1' and '01') collapse to the same numeric minute slot, which the schema forbids. It fails the build so locale data cannot silently lose one of the two entries.

Source

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

            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}'.");
                }

                var canonicalKey = minute.ToString(CultureInfo.InvariantCulture);
                if (normalizedValues.ContainsKey(canonicalKey))
                {
                    throw new InvalidOperationException(
                        $"Locale '{localeCode}.{path}' defines duplicate numeric minute slot {minute}.");
                }

                ValidateMinuteWordValue(localeCode, path, entry.Key, entry.Value, allowEmpty: false);
                normalizedValues[canonicalKey] = entry.Value;
            }

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

        static void ValidateMinuteWordValue(
            string localeCode,
            string path,
            string key,
            SimpleYamlValue value,
            bool allowEmpty)
        {
            if (value is not SimpleYamlScalar scalar || !scalar.IsString)

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Open the locale YAML cited by {path} (e.g. surfaces.clock.minutes) and remove or rename one of the two entries that share the same integer value.
  2. Re-key all minute slots as plain integers with no zero-padding so collisions are visually obvious, then rebuild.
  3. Search the file for the integer form of {minute} to find both spelling variants and keep only one.

Example fix

// before (surfaces.clock.minutes)
minutes:
  01: one minute
  1: a minute
// after
minutes:
  1: one minute
Defensive patterns

Strategy: validation

Validate before calling

# Before editing a clock minutes mapping, confirm each integer slot appears once.
# PowerShell check against the parsed YAML keys:
$keys = @('1','01','2','02')
$dups = $keys | ForEach-Object { [int]::Parse($_) } | Group-Object | Where-Object { $_.Count -gt 1 }
if ($dups) { Write-Error "Duplicate minute slot: $($dups.Name)" }

Prevention

When it happens

Trigger: A locale YAML under src/Humanizer/Loccales defines surfaces.clock.minutes with two keys that parse to the same integer (e.g. '01: one' and '1: one', or '1' plus '1.0'). NormalizeMinuteWordsMapping builds an ordinal dictionary keyed by minute.ToString() and detects the collision at line 329-332.

Common situations: A contributor copies minute entries from another locale and forgets to delete the original; zero-padding keys inconsistently ('5' vs '05'); merging two locales whose minute tables overlap. Fires during 'dotnet build'/'dotnet pack' of the Humanizer project, not at consumer runtime.

Related errors


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