Humanizr/Humanizer · error · InvalidOperationException
Phrase '{path}' must be a scalar string.
Error message
Phrase '{path}' must be a scalar string. What it means
Thrown by GetOptionalLiteral when a YAML key that must hold a scalar string (e.g. symbol, default, beforeCount, now) is instead a mapping or sequence. Literal phrase fields only accept scalars (an unquoted 'null' is treated as absent). Surfaced as compiler diagnostic HSG003 (severity Error).
Source
Thrown at src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs:478
if (!placeholders.Contains(placeholderName))
{
placeholders.Add(placeholderName);
}
}
return new NamedTemplatePhrase(templateName, template, placeholders.ToImmutable());
}
static string? GetOptionalLiteral(SimpleYamlMapping mapping, string key, string path, params string[] allowedPlaceholders)
{
if (!mapping.TryGetValue(key, out var value))
{
return null;
}
if (value is not SimpleYamlScalar scalar)
{
throw new InvalidOperationException($"Phrase '{path}' must be a scalar string.");
}
if (!scalar.IsQuoted && string.Equals(scalar.Value, "null", StringComparison.Ordinal))
{
return null;
}
return ValidateLiteralText(path, scalar.Value, allowedPlaceholders);
}
static string? GetOptionalLiteralOrExplicitTemplate(SimpleYamlMapping mapping, string key, string path)
{
if (!mapping.TryGetValue(key, out var value))
{
return null;
}
if (value is SimpleYamlScalar scalar)View on GitHub (pinned to ffc2b77c0f)
Solutions
- Replace the mapping/sequence with a scalar string value for that key.
- Fix YAML indentation so sibling keys are not accidentally nested under the literal field.
- Move the nested content to the correct parent (e.g. under 'forms:') if it was meant to be form data.
- Rebuild to confirm the diagnostic clears.
Example fix
# before
dataUnits:
byte:
symbol:
singular: B
# after
dataUnits:
byte:
symbol: B Defensive patterns
Strategy: validation
Validate before calling
# Literal fields (symbol, default, zero, singular, dual, paucal, plural, many,
# beforeCount, afterCount, now, today, never) must be scalars.
yq -o=json '[.. | select(tag == "!!map") | to_entries[] | select(.key | test("^(symbol|default|zero|singular|dual|paucal|plural|many|beforeCount|afterCount|now|today|never)$")) | select(.value | tag != "!!str")] ' src/Humanizer/Locales/<code>.yml Prevention
- Keep literal fields as single-line scalars.
- Watch indentation so the next unit does not nest under a literal key.
- Build after structural edits.
When it happens
Trigger: Writing 'default: singular: ...' under a field that expects a plain string, or putting a list where a scalar is required; wrong indentation merging a child mapping under a literal key.
Common situations: Indentation slip that nests the next unit's keys under the current literal; author treats a literal field as a forms block; copy-paste of a mapping into a scalar slot.
Related errors
- Phrase '{path}' must define forms, 'symbol', or 'template'.
- Phrase '{path}' must define forms.
- Phrase '{path}' must define 'numeric', 'text', or 'words'.
- Phrase forms '{path}' must define at least one form.
- Phrase '{path}.countPlacement' uses unsupported count placem
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/cdacc88d5fa8078a.
Report an issue: GitHub.