twigphp/Twig · error · SyntaxError

Regexp " " passed to "matches" is not valid: .

Error message

Regexp "%s" passed to "matches" is not valid: %s.

What it means

The `matches` operator in Twig applies a PCRE regexp via preg_match. When the right-hand operand is a constant string, the node constructor eagerly validates it during compilation; if preg_match emits a warning (bad delimiter, unknown modifier, etc.) the error handler converts it into this SyntaxError so the failure surfaces at compile time with the template line.

Solutions

  1. Fix the constant regexp in the template: wrap it in valid delimiters (e.g. `/pattern/`) and remove unsupported flags like `g`.
  2. Test the pattern with preg_match in plain PHP or a regex tool to confirm it compiles under PCRE.
  3. If the regexp is dynamic, pass it as a variable expression instead of a constant (validation then happens at runtime).

Example fix

// before (template)
{% if email matches '\\S+@\\S+' %}
// after
{% if email matches '/\\S+@\\S+/' %}
Defensive patterns

Strategy: validation

Validate before calling

function isValidTwigRegex(string $pattern): bool {
    set_error_handler(static fn () => false);
    try { preg_match($pattern, ''); return preg_last_error() === PREG_NO_ERROR; }
    finally { restore_error_handler(); }
}
if (!isValidTwigRegex('/^\\S+@\\S+$/')) { /* fix pattern before embedding in template */ }

Prevention

When it happens

Trigger: Compiling a template like `{{ name matches '/foo/' }}` where the constant regex is malformed: missing delimiters, `'/foo/g'` (unknown modifier g), unbalanced parentheses, or invalid escape sequences. Also constructing MatchesBinary with a ConstantExpression holding an invalid regexp in PHP.

Common situations: Copy-pasting regexes from JavaScript (with `/g` or `/i` misplacement), forgetting delimiters (`'foo+'` instead of `'/foo+/'`), or quoting mistakes in template strings that break the pattern.

Related errors


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/9901580152000891. Report an issue: GitHub.

Appendix: source

Thrown at src/Node/Expression/Binary/MatchesBinary.php:35

use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\ReturnBoolInterface;
use Twig\Node\Node;

class MatchesBinary extends AbstractBinary implements ReturnBoolInterface, CoercesChildrenToStringInterface
{
    public function __construct(Node $left, Node $right, int $lineno)
    {
        if (!$left instanceof AbstractExpression) {
            trigger_deprecation('twig/twig', '3.24', 'Passing a "%s" instance to "%s()" first argument is deprecated, pass an "AbstractExpression" instance instead.', $left::class, __METHOD__);
        }
        if (!$right instanceof AbstractExpression) {
            trigger_deprecation('twig/twig', '3.24', 'Passing a "%s" instance to "%s()" second argument is deprecated, pass an "AbstractExpression" instance instead.', $right::class, __METHOD__);
        }

        if ($right instanceof ConstantExpression) {
            $regexp = $right->getAttribute('value');
            set_error_handler(static fn ($t, $m) => throw new SyntaxError(\sprintf('Regexp "%s" passed to "matches" is not valid: %s.', $regexp, substr($m, 14)), $lineno));
            try {
                preg_match($regexp, '');
            } finally {
                restore_error_handler();
            }
        }

        parent::__construct($left, $right, $lineno);
    }

    public function compile(Compiler $compiler): void
    {
        $compiler
            ->raw('CoreExtension::matches(')
            ->subcompile($this->getNode('right'))
            ->raw(', ')
            ->subcompile($this->getNode('left'))
            ->raw(')')

View on GitHub (pinned to a414c3a491)