Humanizr/Humanizer · error · InvalidOperationException

Template '{path}' can only use placeholders: {allowedPlaceho

Error message

Template '{path}' can only use placeholders: {allowedPlaceholders}.

What it means

Thrown by CreateNamedTemplatePhrase when a template uses a placeholder name that is syntactically valid but not in the allowed set for that surface. Each surface restricts templates to specific names (e.g. dataUnit/timeUnit templates allow count and unit; counted-phrase templates allow count, unit, prep). Surfaced as compiler diagnostic HSG003 (severity Error).

Source

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

        {
            var placeholders = ImmutableArray.CreateBuilder<string>();

            foreach (var placeholderName in GetPlaceholderNames(template))
            {
                if (int.TryParse(placeholderName, out _))
                {
                    throw new InvalidOperationException($"Template '{path}' cannot use numeric placeholder '{{{placeholderName}}}'.");
                }

                if (placeholderName.Length == 0 || !char.IsLetter(placeholderName[0]) || placeholderName.Any(static c => !char.IsLetterOrDigit(c) && c != '_'))
                {
                    throw new InvalidOperationException($"Template '{path}' uses unsupported placeholder '{{{placeholderName}}}'.");
                }

                if (!allowedPlaceholders.Contains(placeholderName, StringComparer.Ordinal))
                {
                    throw new InvalidOperationException(
                        $"Template '{path}' can only use placeholders: {string.Join(", ", allowedPlaceholders.Select(static value => $"{{{value}}}"))}.");
                }

                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;
            }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Read the diagnostic: it lists the allowed placeholders for that exact path.
  2. Replace the disallowed placeholder with one from the allowed set, or remove it.
  3. Rebuild to confirm the diagnostic clears.

Example fix

# before (data unit template)
dataUnits:
  byte:
    template:
      value: '{value}'
# after
dataUnits:
  byte:
    template:
      value: '{count} {unit}'
Defensive patterns

Strategy: validation

Validate before calling

# Per-surface allowed placeholders:
#   dataUnits/timeUnits templates: {count}, {unit}
#   counted-phrase templates: {count}, {unit}, {prep}
#   duration 'age' template: {value}
# Review each template value against its surface's allow-list before building.

Prevention

When it happens

Trigger: Writing a data-unit template with '{value}' or '{name}', or a duration template (allowed placeholders: none) with any placeholder.

Common situations: Author reuses a placeholder valid on one surface on a different surface where it is not supported; assuming all surfaces share the same placeholder vocabulary.

Related errors


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