twigphp/Twig · error · RuntimeError

Regexp " " passed to "matches" failed: .

Error message

Regexp "%s" passed to "matches" failed: %s.

What it means

This error comes from Twig's `matches` operator implementation (CoreExtension::matches). Twig first validates the regular expression via preg_match with a temporary error handler; if preg_match itself returns false (PCRE executed but failed), Twig throws this RuntimeError with preg_last_error_msg() describing the PCRE failure. It indicates the pattern was syntactically accepted but the match operation failed at the PCRE engine level (e.g. backtracking limit exceeded).

Solutions

  1. Simplify the regex to avoid catastrophic backtracking (replace nested quantifiers, use atomic groups or possessive quantifiers)
  2. Increase pcre.backtrack_limit in php.ini or via ini_set before rendering (e.g. ini_set('pcre.backtrack_limit', '10000000'))
  3. Pre-validate the pattern and subject sizes before running `matches`, splitting large subjects into chunks
  4. Log preg_last_error() alongside the message to identify the exact PCRE error code

Example fix

// before
{% if description matches '/^(a+)+$/' %}...{% endif %}
// after (in a PHP extension/twig environment)
ini_set('pcre.backtrack_limit', '10000000');
// or simplify the pattern
{% if description matches '/^a+$/' %}...{% endif %}
Defensive patterns

Strategy: try-catch

Validate before calling

if (@preg_match($regexp, '') === false || preg_last_error() !== PREG_NO_ERROR) {
    throw new \InvalidArgumentException('Unsafe or pathological regex');
}

Try / catch

try {
    $ok = preg_match($regexp, $subject);
    if ($ok === false) {
        throw new \RuntimeException(preg_last_error_msg());
    }
} catch (\Throwable $e) {
    ini_set('pcre.backtrack_limit', '10000000');
    // retry with simplified pattern or fail gracefully in template
}

Prevention

When it happens

Trigger: Calling Twig's `matches` operator (twig_matches -> CoreExtension::matches) when preg_match($regexp, $str ?? '') returns false: typically PREG_BACKTRACK_LIMIT_ERROR (catastrophic backtracking on a complex pattern/large subject) or PREG_RECURSION_LIMIT_ERROR. Note that syntactically invalid patterns raise the separate 'is not valid' error via the custom error handler, not this one.

Common situations: Templates running user-supplied regexes against large strings; regexes with nested quantifiers like (a+)+ causing backtrack limit exhaustion on shared hosting with low pcre.backtrack_limit; patterns that are valid but too complex for the subject size.

Related errors


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

Appendix: source

Thrown at src/Extension/CoreExtension.php:1200

        }

        // fallback to <=>
        return $a <=> $b;
    }

    /**
     * @throws RuntimeError When the regular expression cannot be evaluated
     *
     * @internal
     */
    public static function matches(string $regexp, ?string $str): int
    {
        set_error_handler(static function ($t, $m) use ($regexp) {
            throw new RuntimeError(\sprintf('Regexp "%s" passed to "matches" is not valid', $regexp).substr($m, 12));
        });
        try {
            if (false === $result = preg_match($regexp, $str ?? '')) {
                throw new RuntimeError(\sprintf('Regexp "%s" passed to "matches" failed: %s.', $regexp, preg_last_error_msg()));
            }

            return $result;
        } finally {
            restore_error_handler();
        }
    }

    /**
     * Returns a trimmed string.
     *
     * @param string|\Stringable|null $string
     * @param string|null             $characterMask
     * @param string                  $side          left, right, or both
     *
     * @throws RuntimeError When an invalid trimming side is used
     *
     * @internal

View on GitHub (pinned to a414c3a491)