Atomic Groups
debt(d6/e3/b3/t7)
Closest to 'specialist tool catches' (d5), bumped +1 because while redos-detector/recheck/semgrep can flag catastrophic backtracking patterns, detecting semantic differences from misplaced atomic groups typically escapes standard scanners and only surfaces under targeted ReDoS testing or careful review.
Closest to 'simple parameterised fix' (e3), per quick_fix: wrap the offending greedy subpattern in (?>...). It's more than a one-line swap because you must identify the correct subexpression and verify semantics didn't change, but it stays localized to the regex string.
Closest to 'localised tax' (b3), the atomic group lives inside a single regex pattern in one component; it doesn't propagate architectural weight across the codebase even though applies_to spans multiple contexts.
Closest to 'serious trap' (t7), grounded in misconception: developers assume (?>...) is purely a performance tweak like (?:...) but it changes match semantics by discarding backtrack positions, causing legitimate matches to fail — this contradicts the intuition built from non-capturing groups.
Also Known As
TL;DR
Explanation
An atomic group (?>...) matches like a non-capturing group but throws away all backtracking positions inside the group once it has matched successfully. This means the regex engine will not re-enter the group to try alternative matches, even if the overall pattern later fails. Atomic groups are a possessive form of grouping — the engine commits to the match and moves on. They are supported natively in PCRE (PHP), Java, .NET, Ruby, Perl, and PCRE2. JavaScript's built-in RegExp does not support them, and Python's standard `re` module does not either (the third-party `regex` module does). Atomic groups are the primary tool for defusing catastrophic backtracking in patterns that would otherwise explore exponential alternatives. A common example: matching an HTML attribute value with `(?>[^"]*)"` prevents the engine from backtracking through the character class if the closing quote is missing — instead of trying every possible split of the string, it fails fast. Atomic groups often replace possessive quantifiers (`a*+`, `a++`) since (?>a*) is equivalent to a*+ but works in engines that lack possessive support. They are also useful for optimising alternations where you know the first successful branch is the correct one and further exploration is wasted work.
Common Misconception
Why It Matters
Common Mistakes
- Confusing (?>...) with (?:...) — the latter is merely non-capturing, the former also disables backtracking inside.
- Placing atomic groups around alternations where later branches were intended as fallbacks, causing legitimate matches to fail.
- Assuming atomic groups work in every regex flavour — older JavaScript engines and Python's `re` module do not support them (Python's `regex` third-party module does).
- Wrapping the wrong subexpression: putting (?>...) around a fixed literal has no effect because there is nothing to backtrack over.
- Using atomic groups everywhere as a blanket optimisation instead of profiling to find the real backtracking hotspot.
Code Examples
<?php
// Vulnerable to catastrophic backtracking on long non-matching input.
// For input like 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa!' the engine tries
// every possible split of the a's before failing - exponential time.
$pattern = '/^(a+)+$/';
$input = str_repeat('a', 30) . '!';
preg_match($pattern, $input); // may hang the request
<?php
// Atomic group prevents the engine from re-splitting the a's.
// Once (?>a+) consumes as many a's as it can, it never gives any back,
// so the outer + cannot generate the exponential alternatives.
$pattern = '/^(?>a+)+$/';
$input = str_repeat('a', 30) . '!';
preg_match($pattern, $input); // fails in linear time
// Real-world: match a quoted attribute value without ReDoS risk
$attr = '/"(?>[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"/';