Humanizr/Humanizer · error · InvalidOperationException

Phrase '{path}' must define 'single', 'multiple', or 'templa

Error message

Phrase '{path}' must define 'single', 'multiple', or 'template'.

What it means

Thrown by LocalePhraseNormalization.ParseDateHumanizePhrase when a relative-date phrase entry is a mapping but contains none of the allowed keys ('single', 'multiple', 'template'). An empty mapping (or one with only unknown keys, which are rejected earlier) is invalid because the phrase must carry at least one form to render.

Source

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

        {
            if (value is SimpleYamlScalar scalar)
            {
                return new DateHumanizePhrase(ValidateLiteralText(path, scalar.Value));
            }

            var mapping = ExpectMapping(value, path);
            RejectUnknownKeys(mapping, path, ["single", "multiple", "template"]);

            var single = GetOptionalLiteral(mapping, "single", $"{path}.single");
            var multiple = mapping.TryGetValue("multiple", out var multipleValue)
                ? ParseCountedPhrase(multipleValue, $"{path}.multiple")
                : null;
            var template = mapping.TryGetValue("template", out var templateValue)
                ? ParseNamedTemplate(templateValue, $"{path}.template", [])
                : null;
            if (single is null && multiple is null && template is null)
            {
                throw new InvalidOperationException($"Phrase '{path}' must define 'single', 'multiple', or 'template'.");
            }

            return new DateHumanizePhrase(single, multiple, template);
        }

        static TimeSpanPhraseSet ParseTimeSpan(
            string localeCode,
            SimpleYamlValue value,
            string path,
            bool preserveDurationCaseForms)
        {
            var mapping = ExpectMapping(value, path);
            var builder = ImmutableDictionary.CreateBuilder<string, TimeSpanPhrase>(StringComparer.Ordinal);

            foreach (var entry in mapping.Values)
            {
                if (entry.Key is "zero" or "age")
                {

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. In the named phrase path, add at least one of 'single', 'multiple', or 'template'.
  2. Check for typos in key names; unknown keys are rejected before this check, so the path message pinpoints the location.
  3. Rebuild to confirm the generator accepts the phrase.

Example fix

# before
relativeDate:
  past:
    day:
      {}
# after
relativeDate:
  past:
    day:
      single: 'yesterday'
Defensive patterns

Strategy: validation

Validate before calling

# For each relativeDate phrase mapping, confirm at least one of the allowed
# keys is present:
#   rg -A3 "relativeDate" src/Humanizer/Locales/<code>.yml
# Every non-scalar phrase entry needs single, multiple, or template.

Prevention

When it happens

Trigger: A locale YAML defines a relativeDate phrase unit (e.g. past.day) as a mapping that omits single, multiple, and template. After RejectUnknownKeys filters stray keys, all three optionals resolve to null and the check fires.

Common situations: A contributor adds a phrase entry with a wrong key name (e.g. 'one' instead of 'single'), or an empty mapping placeholder, then runs the build. The error names the phrase path (e.g. en.phrases.relativeDate.past.day).

Related errors


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