Humanizr/Humanizer · error · InvalidOperationException

Phrase section '{path}' must be a mapping.

Error message

Phrase section '{path}' must be a mapping.

What it means

Thrown by ExpectMapping when a YAML node that must be a mapping (object) is instead a scalar or sequence. Many phrase sections (relativeDate, duration, dataUnits, timeUnits, forms, template blocks) require mappings. Surfaced as compiler diagnostic HSG003 (severity Error).

Source

Thrown at src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs:544

                {
                    throw new InvalidOperationException(
                        allowedPlaceholders.Length == 0
                            ? $"Phrase '{path}' can only use placeholders in explicit 'template' entries."
                            : $"Phrase '{path}' can only use placeholders: {string.Join(", ", allowedPlaceholders.Select(static value => $"{{{value}}}"))}.");
                }
            }

            return text;
        }

        static IEnumerable<string> GetPlaceholderNames(string text) =>
            PlaceholderRegex.Matches(text)
                .Cast<Match>()
                .Select(static match => match.Groups["name"].Value);

        static SimpleYamlMapping ExpectMapping(SimpleYamlValue value, string path) =>
            value as SimpleYamlMapping ??
            throw new InvalidOperationException($"Phrase section '{path}' must be a mapping.");

        static void RejectUnknownKeys(SimpleYamlMapping mapping, string path, params string[] allowedKeys)
        {
            foreach (var key in mapping.Values.Keys.Where(key => !allowedKeys.Contains(key, StringComparer.Ordinal)))
            {
                throw new InvalidOperationException(
                    $"Phrase section '{path}' defines unsupported property '{key}'. Supported properties: {string.Join(", ", allowedKeys)}.");
            }
        }
    }
}

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Restructure the node as a mapping with the expected keys (see the path in the message).
  2. If a scalar is genuinely intended, move it to the field that accepts scalars (e.g. a unit value rather than the duration section).
  3. Rebuild to confirm the diagnostic clears.

Example fix

# before
duration: hours
# after
duration:
  hour:
    forms:
      default: hours
Defensive patterns

Strategy: validation

Validate before calling

# Sections that must be mappings: relativeDate, duration, dataUnits, timeUnits,
# past, future, forms, and any template block. Confirm their YAML tag is !!map.
yq -o=json '[.phrases.relativeDate, .phrases.duration, .phrases.dataUnits, .phrases.timeUnits] | map(select(. != null and tag != "!!map"))' src/Humanizer/Locales/<code>.yml

Prevention

When it happens

Trigger: Writing 'duration: hours' (scalar) where a mapping of unit keys is expected, or 'forms: [a, b]' (sequence) where a mapping of form keys is expected.

Common situations: Author oversimplifies a section into a scalar; misreads the schema; YAML flow-style mistake producing a sequence; indentation turning a mapping into a scalar sibling.

Related errors


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