oracle/graal · error · UnsupportedRegexException
nested look-behind assertions
Error message
nested look-behind assertions
What it means
Thrown by RegexAST.createPrefix when the AST reports nested look-behind assertions while building the search prefix. For find()-style searches the engine precomputes a fixed-length prefix of the regex to rewind input safely before fromIndex; that construction only supports flat look-behinds, so nested ones are rejected.
Source
Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/ast/RegexAST.java:562
* -> result: /(?:[_any_][_any_](?:|[_any_](?:|[_any_])))(?<=ab)/
* -> the non-optional [_any_] - matchers will be used if fromIndex > 0,
* the optional matchers will always be used
* }
*
* The non-optional [_any_] - matchers are also called "hard prefix states" in other parts of
* the code. They make sure we only match look-behinds and not parts of the main expression when
* starting a match before the fromIndex-parameter. For example, when matching a regex
* /(?<=ab)c|ab/ on string "abc" with fromIndex 2, we would first rewind the index to 0 in order
* to match the look-behind, but we must make sure not to match the literal "ab" of the main
* expression's second branch until we reach index 2 again.
*/
public void createPrefix() {
if (root.startsWithCaret() || properties.hasNonLiteralLookBehindAssertions()) {
wrappedRoot = root;
return;
}
if (properties.hasNestedLookBehindAssertions()) {
throw new UnsupportedRegexException("nested look-behind assertions");
}
final int prefixLength = root.getPrefixLengthMax();
if (prefixLength == 0) {
wrappedRoot = root;
return;
}
final Group wrapRoot = createGroup();
wrapRoot.setPrefix();
final Sequence wrapRootSeq = createSequence();
wrapRoot.add(wrapRootSeq);
wrapRootSeq.setPrefix();
// create non-optional matchers ([_any_][_any_]...)
for (int i = 0; i < prefixLength; i++) {
wrapRootSeq.add(createPrefixAnyMatcher());
}
if (!flags.isSticky()) {
Group prevOpt = null;
// create optional matchers ((?:|[_any_](?:|[_any_]))...)View on GitHub (pinned to a66e9ccd1d)
Solutions
- Flatten the nesting: inline the inner look-behind condition into a single look-behind or move it into the main expression.
- Perform the inner check in application code after locating a candidate match.
- Rely on the backtracking-engine fallback (TRegexCompilationRequest catches UnsupportedRegexException and retries) — do not force the linear engine.
- Use matches() on extracted substrings instead of find() with look-behind, so no prefix needs to be built.
Example fix
// before
String pattern = "(?<=(?<=foo)bar)baz"; Matcher m = p.matcher(input); m.find(from);
// after
String pattern = "barbaz"; // match candidate, then verify input.startsWith("foo", m.start()-6) in code Defensive patterns
Strategy: try-catch
Validate before calling
if (pattern.indexOf("(?<=") >= 0 && countOccurrences(pattern, "(?<=") > 1) { throw new IllegalArgumentException("possible nested look-behind; prefix construction unsupported"); } Type guard
null
Try / catch
try { engine.compile(pattern); } catch (UnsupportedRegexException e) { backtrackingEngine.compile(pattern); } // or verify look-behind conditions manually after find() Prevention
- Keep look-behinds unnested and at top level.
- For find() with fromIndex, verify preceding text in code instead of look-behinds.
- Watch for nesting introduced by group wrapping of look-behinds.
When it happens
Trigger: Compiling for searching (find/fromIndex) a regex that contains a look-behind nested inside another look-around or group in a way flagged by ASTProperties.hasNestedLookBehindAssertions, e.g. (?<=(?<=a)b) or (?=(?<=x))y.
Common situations: Search-oriented APIs (find with fromIndex) on patterns ported from PCRE/Perl that stack look-behinds; look-behind inside quantified groups; syntax-highlighting or log-scanning grammars using nested variable-position assertions.
Related errors
- nested look-behind assertions
- DFA transition size explosion
- too many quantifiers
- Regex with overlapping bounded quantifier (after look-ahead
- too many transitions
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/b0cecd1328e555b4.
Report an issue: GitHub.