oracle/graal · error · UnsupportedRegexException

empty path with look-ahead assertion in expression with boun

Error message

empty path with look-ahead assertion in expression with bounded quantifier

What it means

Thrown by NFATraversalRegexASTVisitor when, while building the DFA, it finds a look-ahead assertion sitting on a path that consumes no characters inside a mandatory quantifier loop. Scanning backwards from the group-enter of curGroup to the current node, any look-ahead node on that zero-width path makes the transition constraints unresolvable for the linear engine, so the regex is rejected.

Source

Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/ast/visitors/NFATraversalRegexASTVisitor.java:620

                        throw new UnsupportedRegexException("Cannot compile regex with empty state to DFA/NFA");
                    }
                    popGroupExit();
                    cur = curTerm;
                    // Set the current group node as the path's target to indicate we want to
                    // generate an EMPTY_STATE for it. The empty state allows the backtracking
                    // engine to loop without consuming characters.
                    curPath.add(PathElement.create(cur));
                    return true;
                }
                if (isBuildingDFA() && curGroup.isMandatoryQuantifier() && !lookAroundsOnPath.isEmpty()) {
                    for (int i = curPath.length() - 1; i >= 0; i--) {
                        long element = curPath.get(i);
                        RegexASTNode node = pathGetNode(element);
                        if (PathElement.isGroupEnter(element) && node == curGroup) {
                            break;
                        }
                        if (node.isLookAheadAssertion()) {
                            throw new UnsupportedRegexException("empty path with look-ahead assertion in expression with bounded quantifier");
                        }
                    }
                }
                // otherwise, retreat.
                return retreat();
            }
            Sequence parentSeq = (Sequence) curTerm.getParent();
            if (curTerm == (forward ? parentSeq.getLastTerm() : parentSeq.getFirstTerm())) {
                final Group parentGroup = parentSeq.getParent();
                pushGroupExit(parentGroup);
                if (parentGroup.isLoop()) {
                    cur = parentGroup;
                    return false;
                }
                curTerm = parentGroup;
            } else {
                cur = parentSeq.getTerms().get(curTerm.getSeqIndex() + (forward ? 1 : -1));
                return false;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Pull the look-ahead out of the quantified group: ((?=x)){3} is equivalent to (?=x), so write it once.
  2. Ensure the loop body consumes at least one character, e.g. ((?:.)(?=x)){3} instead of ((?=x)){3}.
  3. Rely on backtracking-engine fallback, which supports this construct.
  4. Validate the assertion in application code after a candidate match.

Example fix

// before
String pattern = "((?=.*\d)){2}\w+";

// after
String pattern = "(?=.*\d)\w+"; // assertion applied once, outside the quantifier
Defensive patterns

Strategy: try-catch

Validate before calling

// detect a look-ahead wrapped in a quantified group: ((?=...)){n} / ((?=...))+
if (Pattern.compile("\\(\\(\\?=").matcher(pattern).find()) throw new IllegalArgumentException("quantified look-ahead group; unsupported in linear engine");

Type guard

null

Try / catch

try { compileLinear(pattern); } catch (UnsupportedRegexException e) { compileBacktracking(pattern); } // or hoist the look-ahead out of the quantifier

Prevention

When it happens

Trigger: Compiling in DFA mode a pattern like ((?=x)){3} or (a?(?=y)){2,4} — a mandatory loop containing a look-ahead that can be reached without consuming input.

Common situations: Validation grammars that repeat assertion-only groups (e.g. ((?=.*\d)){2}); wrapping look-aheads in counted or mandatory groups for readability; generated patterns that quantifier-wrap every assertion.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/5ce962c616b74659. Report an issue: GitHub.