How to Fix a Regular Expression That Hangs
A regex that hangs is backtracking catastrophically: nested quantifiers give the engine an exponential number of ways to split the input, and it tries them all before admitting failure. The signature is a quantifier inside a group that is itself quantified, such as (a+)+, combined with input that almost matches. Rewriting to remove the ambiguity fixes it.
Regex Tester
Test a regular expression and see every match highlighted.
The alarming property is how suddenly it appears. A pattern runs instantly on twenty characters and appears to hang forever on thirty, because each additional character can double the work. Nothing looks wrong in the pattern itself.
It only bites on input that nearly matches. A string that fails immediately is cheap to reject; a string that matches most of the pattern and then fails at the very end forces the engine to reconsider every earlier decision.
Step by step
-
Look for a quantifier inside a quantified group
Patterns shaped like (x+)+, (x*)*, or (x|xy)+ are the classic offenders. Each gives the engine more than one way to divide the same text, and the number of divisions grows exponentially with length.
-
Test with input that almost matches
Take a string that satisfies the pattern up to the final character, then break it. Add characters one at a time. If the time roughly doubles per character rather than growing gently, the pattern backtracks catastrophically.
-
Make the alternatives mutually exclusive
Rewrite so only one path can match any given character. Replacing (\d+|\w+)+ with \w+ removes the ambiguity entirely, because a digit is already a word character and the two branches were competing for the same input.
-
Replace dot-star with a negated class
Inside delimiters, "[^"]*" is dramatically faster and safer than ".*?" because it cannot cross the closing delimiter and so has nothing to backtrack over. This one substitution fixes a large share of slow patterns.
-
Anchor the pattern
Without an anchor, a failed match is retried at every starting position in the string, multiplying the cost by the input length. Adding ^ or \b where the match must begin turns many quadratic scans into linear ones.
Example
Both patterns validate the same thing. The first is exponential on failure; the second is linear.
Dangerous
^(a+)+$
matched against "aaaaaaaaaaaaaaaaaaaaaaaaX"
Safe rewrite
^a+$
same result, no backtracking
Frequently asked questions
What is catastrophic backtracking?
Why does it only happen on some inputs?
Is this a security problem?
Do all regex engines suffer from this?
How do I test whether a pattern is safe?
Tools used in this guide
All tools →Related guides
All guides →Last reviewed .