Humanizr/Humanizer · error · InvalidOperationException
Phrase '{path}' cannot use numeric placeholder '{{placeholde
Error message
Phrase '{path}' cannot use numeric placeholder '{{placeholder}}'. What it means
Thrown by ValidateLiteralText when a literal phrase string (a non-template field like singular, default, beforeCount) contains a numeric placeholder such as {0}. Literals do not support positional placeholders; only explicit 'template' entries do formatting. Surfaced as compiler diagnostic HSG003 (severity Error).
Source
Thrown at src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs:517
}
var templateMapping = ExpectMapping(value, path);
RejectUnknownKeys(templateMapping, path, ["template"]);
if (!templateMapping.TryGetValue("template", out var templateValue))
{
throw new InvalidOperationException($"Phrase '{path}' must define a scalar string or an explicit 'template'.");
}
return ParseNamedTemplate(templateValue, $"{path}.template", ["value"]).Template;
}
static string ValidateLiteralText(string path, string text, params string[] allowedPlaceholders)
{
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;
}View on GitHub (pinned to ffc2b77c0f)
Solutions
- Remove the placeholder from the literal, or move the formatting into an explicit 'template' entry that uses named placeholders.
- If the count must appear in the phrase, use countPlacement + forms instead of inlining {0}.
- Rebuild to confirm the diagnostic clears.
Example fix
# before
duration:
hour:
multiple:
forms:
default: '{0} hours'
# after
duration:
hour:
multiple:
countPlacement: before
forms:
default: hours Defensive patterns
Strategy: validation
Validate before calling
# Find numeric placeholders in literal (non-template) string fields.
grep -nE '(symbol|default|zero|singular|dual|paucal|plural|many|beforeCount|afterCount|now|today|never):.*\{[0-9]+\}' src/Humanizer/Locales/<code>.yml Prevention
- Literals are plain text; use countPlacement + forms or an explicit template for interpolation.
- Do not paste string.Format patterns into literal fields.
- Build after migrating resources.
When it happens
Trigger: Writing 'default: {0} hours' or 'beforeCount: {0}' in any phrase literal field.
Common situations: Copy-paste from string.Format-based resources; habit from other i18n libraries; converting old code that used numeric substitution.
Related errors
- Phrase '{path}' uses unsupported placeholder '{{placeholder}
- Phrase '{path}' can only use placeholders in explicit 'templ
- Phrase '{path}' can only use placeholders: {allowedPlacehold
- 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/34957713c689e98c.
Report an issue: GitHub.