Humanizr/Humanizer · error · InvalidOperationException

Phrase '{path}' uses unsupported placeholder '{{placeholder}

Error message

Phrase '{path}' uses unsupported placeholder '{{placeholder}}'.

What it means

Thrown by ValidateLiteralText when a literal phrase string contains a placeholder whose name is empty, does not start with a letter, or contains non-identifier characters. Literal placeholders must be valid identifiers. Surfaced as compiler diagnostic HSG003 (severity Error).

Source

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

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

        static IEnumerable<string> GetPlaceholderNames(string text) =>
            PlaceholderRegex.Matches(text)
                .Cast<Match>()
                .Select(static match => match.Groups["name"].Value);

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Remove the malformed placeholder from the literal, or use only an allowed identifier placeholder where permitted.
  2. Move dynamic formatting into an explicit 'template' entry if a placeholder is genuinely needed.
  3. Rebuild to confirm the diagnostic clears.

Example fix

# before
forms:
  default: '{ count } hours'
# after
forms:
  default: hours
Defensive patterns

Strategy: validation

Validate before calling

# Find malformed placeholders inside literal string fields.
grep -nE '(symbol|default|zero|singular|dual|paucal|plural|many|beforeCount|afterCount):.*\{(\s*|[^A-Za-z_][^}]*|[^}]*[^A-Za-z0-9_][^}]*)\}' src/Humanizer/Locales/<code>.yml

Prevention

When it happens

Trigger: Writing a literal with '{}', '{ count}', '{count-unit}', or '{count!}'.

Common situations: Stray whitespace or punctuation inside braces; hyphenated placeholder names; leftover brace pairs.

Related errors


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