Humanizr/Humanizer · error · InvalidOperationException

Template '{path}' cannot use numeric placeholder '{{placehol

Error message

Template '{path}' cannot use numeric placeholder '{{placeholderName}}'.

What it means

Thrown by CreateNamedTemplatePhrase when a template 'value' string contains a numeric placeholder like {0} or {1}. Humanizer templates use named placeholders (e.g. {count}, {unit}), never positional indices. Surfaced as compiler diagnostic HSG003 (severity Error).

Source

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

            RejectUnknownKeys(mapping, path, ["name", "value"]);
            var name = GetOptionalLiteral(mapping, "name", $"{path}.name");
            if (!mapping.TryGetValue("value", out var templateValue) || templateValue is not SimpleYamlScalar templateScalar)
            {
                throw new InvalidOperationException($"Template '{path}' must define a scalar 'value'.");
            }

            return CreateNamedTemplatePhrase(name, templateScalar.Value, path, allowedPlaceholders);
        }

        static NamedTemplatePhrase CreateNamedTemplatePhrase(string? templateName, string template, string path, params string[] allowedPlaceholders)
        {
            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);
                }
            }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Replace numeric placeholders with the allowed named ones for that surface (e.g. {count}, {unit}, {value}, {prep}).
  2. Check the error's sibling message (149) which lists the placeholders permitted at that path.
  3. Rebuild to confirm the diagnostic clears.

Example fix

# before
template:
  value: '{0} {1} ago'
# after
template:
  value: '{count} {unit} ago'
Defensive patterns

Strategy: validation

Validate before calling

# Reject numeric placeholders inside template values.
grep -nE 'template:.*\{[0-9]+\}' src/Humanizer/Locales/<code>.yml
# Also catch block-style: grep -nE 'value:.*\{[0-9]+\}' for value: lines.

Prevention

When it happens

Trigger: Authoring a template value with string.Format-style indices such as '{0} {1}' or '{0}'.

Common situations: Copy-paste from C# string.Format code; habit from other localization systems that use numeric placeholders; misunderstanding the template syntax.

Related errors


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