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
- Pull the look-ahead out of the quantified group: ((?=x)){3} is equivalent to (?=x), so write it once.
- Ensure the loop body consumes at least one character, e.g. ((?:.)(?=x)){3} instead of ((?=x)){3}.
- Rely on backtracking-engine fallback, which supports this construct.
- 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
- Never wrap a bare look-ahead in a mandatory or counted group.
- Give assertion-containing loops a consuming element.
- Apply look-aheads once, outside quantifiers.
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
- Regex with overlapping bounded quantifier (after look-ahead
- Cannot compile regex with empty state to DFA/NFA
- Too much additional capture group tracking overhead
- too many capture group transitions
- too many parallel NFA states in one DFA state for bounded qu
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/5ce962c616b74659.
Report an issue: GitHub.