Humanizr/Humanizer · critical · InvalidOperationException

Locale '{localeCode}.surfaces.number.formatting' must define

Error message

Locale '{localeCode}.surfaces.number.formatting' must define at least one property.

What it means

Thrown by ValidateNumberFormattingBlock when the surfaces.number.formatting mapping exists but is empty. The generator requires at least one of the three supported properties (decimalSeparator, negativeSign, groupSeparator) because an empty formatting block carries no information and usually signals a leftover placeholder.

Source

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

                    $"Locale '{localeCode}.{path}.{key}' must be a non-empty string. Omit unused sparse slots instead.");
            }
        }

        static void ValidateNumberFormattingBlock(string localeCode, SimpleYamlMapping formatting)
        {
            foreach (var property in formatting.Values.Keys.Where(static property => property is not ("decimalSeparator" or "negativeSign" or "groupSeparator")))
            {
                throw new InvalidOperationException(
                    $"Locale '{localeCode}.surfaces.number.formatting' defines unsupported property '{property}'. Supported properties: decimalSeparator, negativeSign, groupSeparator.");
            }

            ValidateOptionalFormattingProperty(localeCode, formatting, "decimalSeparator");
            ValidateOptionalFormattingProperty(localeCode, formatting, "negativeSign");
            ValidateOptionalFormattingProperty(localeCode, formatting, "groupSeparator");

            if (formatting.Values.Count == 0)
            {
                throw new InvalidOperationException(
                    $"Locale '{localeCode}.surfaces.number.formatting' must define at least one property.");
            }
        }

        static void ValidateOptionalFormattingProperty(string localeCode, SimpleYamlMapping formatting, string propertyName)
        {
            if (!formatting.TryGetValue(propertyName, out var value))
            {
                return;
            }

            if (value is not SimpleYamlScalar scalar)
            {
                throw new InvalidOperationException(
                    $"Locale '{localeCode}.surfaces.number.formatting.{propertyName}' must be a scalar string, not a mapping or sequence.");
            }

            if (string.IsNullOrEmpty(scalar.Value))

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Populate at least one supported property (most commonly decimalSeparator) under surfaces.number.formatting.
  2. If the locale should not override formatting, remove the entire 'formatting' key so defaults apply.
  3. Rebuild to confirm the error clears.

Example fix

# before
number:
  formatting:
# after
number:
  formatting:
    decimalSeparator: ","
Defensive patterns

Strategy: validation

Validate before calling

# If surfaces.number.formatting exists, it must contain >=1 supported property.
# Pre-build check: ensure the block is either absent or non-empty.

Prevention

When it happens

Trigger: A locale YAML declares 'number:\n formatting: {}' or 'formatting:' with no children. After allow-list filtering, formatting.Values.Count is 0 at CanonicalLocaleAuthoring.cs:374-378 and the build fails.

Common situations: Adding a 'formatting:' header while translating then forgetting to populate it; clearing all three separators during a refactor; an automated migration that stripped values but kept the key.

Related errors


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