symfony/symfony · error · InvalidArgumentException

The "strategy" attribute of the "twig.safe_class" tag on "%s

Error message

The "strategy" attribute of the "twig.safe_class" tag on "%s" contains the invalid strategy "%s"; expected one of "%s" or a custom name matching "[a-z][a-z0-9_]*".

What it means

Thrown by `SafeClassPass::normalizeStrategies()` when a strategy value is not one of the built-in strategies (`html`, `js`, `css`, `url`, `html_attr`, `html_attr_relaxed`, `all`) and also does not match the custom-name regex `[a-z][a-z0-9_]*`. This catches typos and invalid custom strategy names before they silently fail to escape.

Source

Thrown at src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/SafeClassPass.php:57

    }

    private function normalizeStrategies(array $tag, string $id): array
    {
        $strategies = $tag['strategy'] ?? null;
        if (\is_string($strategies)) {
            $strategies = [$strategies];
        } elseif (!\is_array($strategies) || array_filter($strategies, 'is_string') !== $strategies) {
            throw new InvalidArgumentException(\sprintf('The "strategy" attribute of the "twig.safe_class" tag on "%s" must be a string or a list of strings.', $id));
        } elseif (!$strategies) {
            throw new InvalidArgumentException(\sprintf('The "strategy" attribute of the "twig.safe_class" tag on "%s" must not be empty; use "all" to mark the class safe for every strategy.', $id));
        }

        foreach ($strategies as $strategy) {
            if (\in_array($strategy, self::BUILTIN_STRATEGIES, true)) {
                continue;
            }
            if (!preg_match('/^[a-z][a-z0-9_]*$/D', $strategy)) {
                throw new InvalidArgumentException(\sprintf('The "strategy" attribute of the "twig.safe_class" tag on "%s" contains the invalid strategy "%s"; expected one of "%s" or a custom name matching "[a-z][a-z0-9_]*".', $id, $strategy, implode('", "', self::BUILTIN_STRATEGIES)));
            }
        }

        return $strategies;
    }
}

View on GitHub (pinned to 698e28026c)

Solutions

  1. Use a built-in strategy name exactly as listed (`html`, `js`, `css`, `url`, `html_attr`, `html_attr_relaxed`, `all`).
  2. For a custom strategy, use only lowercase letters, digits, and underscores, starting with a letter (e.g. `my_custom`).
  3. Correct the typo to the intended built-in name.

Example fix

# before
services:
    App\Widget:
        tags:
            - { name: twig.safe_class, strategy: HTML }
# after
services:
    App\Widget:
        tags:
            - { name: twig.safe_class, strategy: html }
Defensive patterns

Strategy: validation

Validate before calling

const BUILTIN = ['html','js','css','url','html_attr','html_attr_relaxed','all'];
foreach ((array) $tag['strategy'] as $strategy) {
    if (!in_array($strategy, BUILTIN, true) && !preg_match('/^[a-z][a-z0-9_]*$/D', $strategy)) {
        throw new \InvalidArgumentException(sprintf('Invalid strategy "%s"', $strategy));
    }
}

Type guard

function isValidStrategyName(string $s): bool
{
    $builtin = ['html','js','css','url','html_attr','html_attr_relaxed','all'];
    return in_array($s, $builtin, true) || (bool) preg_match('/^[a-z][a-z0-9_]*$/D', $s);
}

Prevention

When it happens

Trigger: Tagging with `twig.safe_class` and a strategy like `HTML` (uppercase), `html-css` (hyphen), ` html` (leading space), or any value with uppercase/special chars. Line 56-57 validates the regex.

Common situations: Using uppercase or mixed-case strategy names. Adding a hyphen or space in a custom strategy. Typo like `htlm`.

Related errors


AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06). Data as JSON: /api/errors/001d3a65851628da. Report an issue: GitHub.