Humanizr/Humanizer · error · InvalidOperationException

Locale '{localeCode}.headings' must be a mapping.

Error message

Locale '{localeCode}.headings' must be a mapping.

What it means

The 'headings' feature groups calendar heading sequences (month/day names) under 'full' and 'short' sub-keys, so it must be a YAML mapping. A scalar or sequence value has no named sub-properties and is rejected.

Source

Thrown at src/Humanizer.SourceGenerators/Common/LocaleYamlCatalog.cs:787

            return !features.TryGetValue("phrases", out var phraseValue)
                ? null
                : phraseValue is SimpleYamlMapping mapping
                ? LocalePhraseNormalization.Create(localeCode, mapping, preserveDurationCaseForms)
                : throw new InvalidOperationException($"Locale '{localeCode}.phrases' must be a mapping.");
        }

        static HeadingSet? ResolveHeadings(
            string localeCode,
            ImmutableDictionary<string, SimpleYamlValue> features)
        {
            if (!features.TryGetValue("headings", out var headingValue))
            {
                return null;
            }

            if (headingValue is not SimpleYamlMapping mapping)
            {
                throw new InvalidOperationException($"Locale '{localeCode}.headings' must be a mapping.");
            }

            foreach (var property in mapping.Values.Keys.Where(static property => property is not ("full" or "short")))
            {
                throw new InvalidOperationException(
                    $"Locale '{localeCode}.headings' defines unsupported property '{property}'. Supported properties: full, short.");
            }

            return new HeadingSet(
                ParseHeadingSequence(mapping, "full", localeCode),
                ParseHeadingSequence(mapping, "short", localeCode));
        }

        static SimpleYamlMapping? ResolveCalendar(
            string localeCode,
            ImmutableDictionary<string, SimpleYamlValue> features)
        {
            return !features.TryGetValue("calendar", out var calendarValue)

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Wrap the heading data in a mapping with 'full:' and 'short:' keys.
  2. Ensure each sub-key's value is itself a sequence of scalars.
  3. Use an existing locale file (e.g. 'en.yml') as a structural template.

Example fix

# before
headings:
  - January
  - February

# after
headings:
  full:
    - January
    - February
  short:
    - Jan
    - Feb
Defensive patterns

Strategy: validation

Validate before calling

import yaml, pathlib
locales_dir = pathlib.Path('src/Humanizer/Locales')
for f in locales_dir.rglob('*.yml'):
    data = yaml.safe_load(f.read_text())
    if isinstance(data, dict) and 'headings' in data:
        if not isinstance(data['headings'], dict):
            print(f'{f.name}: headings must be a mapping, got {type(data["headings"]).__name__}')

Prevention

When it happens

Trigger: A locale file sets 'headings:' to a scalar or to a bare sequence instead of a mapping containing 'full:' and 'short:' keys.

Common situations: Authoring heading data for the first time and omitting the wrapping mapping; indentation error causing heading sequences to attach at the wrong level.

Related errors


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