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

  1. Remove the placeholder from the literal, or move the formatting into an explicit 'template' entry that uses named placeholders.
  2. If the count must appear in the phrase, use countPlacement + forms instead of inlining {0}.
  3. 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

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


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