Humanizr/Humanizer · error · InvalidOperationException

Case overlay '{path}.phrase' must explicitly define singular

Error message

Case overlay '{path}.phrase' must explicitly define singular and numeric multiple forms.

What it means

Thrown after parsing a 'phrase' value inside a duration-case unit realization when the resulting TimeSpanPhrase object is missing either its singular form or its multiple forms collection. The parser requires both phrase.Single and phrase.Multiple?.Forms to be non-null because case-overlay realizations must fully author both number forms.

Source

Thrown at src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs:786

                throw new InvalidOperationException(
                    $"'{path}' must define exactly one of phrase, sameRenderedAs, notApplicable, or unsupported.");
            }

            if (mapping.TryGetValue("phrase", out var phraseValue))
            {
                var provenance = ParseProvenance(mapping, path, required: true);
                ValidateProvenance(provenance, sources, path);
                ValidateReachableMultipleForms(
                    phraseValue,
                    $"{path}.phrase",
                    requiredMultipleForms);
                var phrase = LocalePhraseNormalization.ParseTimeSpanPhrase(
                    phraseValue,
                    $"{path}.phrase",
                    preserveDuplicateForms: !requiredMultipleForms.IsEmpty);
                if (phrase.Single is null || phrase.Multiple?.Forms is null)
                {
                    throw new InvalidOperationException(
                        $"Case overlay '{path}.phrase' must explicitly define singular and numeric multiple forms.");
                }

                return new DurationCaseUnitRealization(
                    DurationCaseRealizationKind.Authored,
                    null,
                    phrase,
                    null,
                    provenance);
            }

            if (mapping.GetScalar("sameRenderedAs") is { } targetCase)
            {
                var provenance = ParseProvenance(mapping, path, required: true);
                ValidateProvenance(provenance, sources, path);
                return new DurationCaseUnitRealization(
                    DurationCaseRealizationKind.SameRenderedAs,
                    targetCase,

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Inspect the phrase mapping at the reported path and verify it has a 'single' entry and a 'multiple' entry.
  2. Ensure 'multiple' contains a 'forms' mapping (or is itself a mapping of named forms) so that phrase.Multiple.Forms is populated.
  3. Rebuild to confirm both singular and multiple forms are parsed successfully.

Example fix

# before
second:
  phrase:
    single: jedna sekunda
    multiple:
      default: X sekund
# after
second:
  phrase:
    single: jedna sekunda
    multiple:
      forms:
        default: X sekund
        dual: dwie sekundy
Defensive patterns

Strategy: validation

Validate before calling

# Validate that every phrase in a realization has both 'single' and 'multiple.forms'
# Use a YAML parser (e.g. yq or python) before building:
# python -c "import yaml,sys; d=yaml.safe_load(open(sys.argv[1])); ..."
# Ensure each realization unit with 'phrase' has phrase.single and phrase.multiple.forms

Prevention

When it happens

Trigger: A unit realization uses 'phrase:' but the phrase mapping omits the 'single' key or the 'multiple.forms' sub-mapping (or provides them in an incomplete/incorrect shape).

Common situations: A contributor writes a phrase with only a 'multiple' scalar string but no 'single', or forgets the 'forms' sub-key under 'multiple', or copies a base-phrase structure that is valid in phrases.duration (which tolerates partial forms) into a durationCases realization (which requires both).

Related errors


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