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
- Simplify the regex to avoid catastrophic backtracking (replace nested quantifiers, use atomic groups or possessive quantifiers)
- Increase pcre.backtrack_limit in php.ini or via ini_set before rendering (e.g. ini_set('pcre.backtrack_limit', '10000000'))
- Pre-validate the pattern and subject sizes before running `matches`, splitting large subjects into chunks
- 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
- Avoid nested quantifiers and pathological patterns in template regexes
- Keep pcre.backtrack_limit generously configured for large subjects
- Test templates with representative large inputs
- Prefer literal string comparisons or startswith/endswith tests over complex regexes when possible
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
- Regexp " " passed to "matches" is not valid
- Regexp " " passed to "matches" is not valid: .
- Failed to load Twig template
- The "cycle" function expects a non-empty sequence.
- The "random" function cannot pick from an empty sequence or…
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
*
* @internalView on GitHub (pinned to a414c3a491)