Humanizr/Humanizer · error · InvalidOperationException
Phrase '{path}' can only use placeholders: {allowedPlacehold
Error message
Phrase '{path}' can only use placeholders: {allowedPlaceholders}. What it means
Thrown by ValidateLiteralText when a literal phrase string uses a placeholder that is syntactically valid but not in the allowed set for that field (allowedPlaceholders is non-empty but does not contain the name). For example, counted-phrase form literals allow {count} (and {prep} when countPlacement is none) but not {unit}. Surfaced as compiler diagnostic HSG003 (severity Error).
Source
Thrown at src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs:530
{
foreach (var placeholder in GetPlaceholderNames(text))
{
if (int.TryParse(placeholder, out _))
{
throw new InvalidOperationException($"Phrase '{path}' cannot use numeric placeholder '{{{placeholder}}}'.");
}
if (placeholder.Length == 0 || !char.IsLetter(placeholder[0]) || placeholder.Any(static c => !char.IsLetterOrDigit(c) && c != '_'))
{
throw new InvalidOperationException($"Phrase '{path}' uses unsupported placeholder '{{{placeholder}}}'.");
}
if (!allowedPlaceholders.Contains(placeholder, StringComparer.Ordinal))
{
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)))View on GitHub (pinned to ffc2b77c0f)
Solutions
- Read the diagnostic: it lists the placeholders allowed at that path.
- Replace the disallowed placeholder with an allowed one, or remove it.
- Move complex interpolation into an explicit 'template' entry for that phrase.
- Rebuild to confirm the diagnostic clears.
Example fix
# before (counted phrase form, only {count} allowed)
multiple:
forms:
default: '{unit} of {count}'
# after
multiple:
forms:
default: '{count}' Defensive patterns
Strategy: validation
Validate before calling
# Counted-phrase form literals allow {count} (and {prep} when countPlacement is none).
# Flag any other placeholder inside default/singular/plural/etc.
grep -nE '(default|zero|singular|dual|paucal|plural|many):.*\{(?!count|prep$)[A-Za-z_][A-Za-z0-9_]*\}' src/Humanizer/Locales/<code>.yml Prevention
- In counted-phrase forms use only {count} (or {prep} when countPlacement is none).
- For richer interpolation add an explicit 'template' entry.
- Build after editing forms.
When it happens
Trigger: Writing 'default: {unit} hours' in a counted-phrase form literal (only count/prep allowed), or 'beforeCount: {count} {unit}' (only prep allowed there).
Common situations: Author reuses a placeholder valid on templates inside a literal field with a narrower allowance; misunderstanding which fields accept which names.
Related errors
- Phrase '{path}' cannot use numeric placeholder '{{placeholde
- Phrase '{path}' uses unsupported placeholder '{{placeholder}
- Phrase '{path}' can only use placeholders in explicit 'templ
- Template '{path}' cannot use numeric placeholder '{{placehol
- Template '{path}' uses unsupported placeholder '{{placeholde
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/c1bd104868d23f2e.
Report an issue: GitHub.