{"record":{"id":"12912abb2685253e","repo":"oracle/graal","slug":"tracefinder-nfa-transition-explosion","errorCode":null,"errorMessage":"TraceFinder NFA transition explosion","messagePattern":"TraceFinder NFA transition explosion","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/Counter.java","lineNumber":91,"sourceCode":"        count -= i;\n        return ret;\n    }\n\n    public static class ThresholdCounter extends Counter {\n\n        private final int max;\n        private final String errorMsg;\n\n        public ThresholdCounter(int max, String errorMsg) {\n            this.max = max;\n            this.errorMsg = errorMsg;\n        }\n\n        @Override\n        public int inc(int i) {\n            final int ret = super.inc(i);\n            if (getCount() > max) {\n                throw new UnsupportedRegexException(errorMsg);\n            }\n            return ret;\n        }\n    }\n\n    public static class ThreadSafeCounter extends Counter {\n\n        @Override\n        public int inc() {\n            int c = count;\n            if (c < Integer.MAX_VALUE) {\n                count = c + 1;\n            }\n            return count;\n        }\n\n        @Override\n        public int inc(int i) {","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/Counter.java#L73-L109","documentation":"Thrown by NFATraceFinderGenerator.java:98 when the TraceFinder NFA's transition count exceeds TRegexMaxNFASize (3500; the TraceFinder variant caps transitions by the same constant, unlike the main NFA which uses Short.MAX_VALUE). It bounds the auxiliary backward-search automaton used for substring tracing. Exceeding it raises UnsupportedRegexException during compilation.","triggerScenarios":"Compiling a pattern that takes the TraceFinder path (lookbehind/backward search) whose trace NFA accumulates more than 3500 transitions — e.g. lookbehind containing wide character classes or long alternations.","commonSituations":"Lookbehind patterns with big classes (\\p{L}{10,}, wide Unicode classes) on GraalVM guest languages; works when the class is narrowed or the lookbehind shortened.","solutions":["Narrow character classes inside lookbehind (e.g. replace \\p{Any} with the actual expected alphabet).","Move the length/lookbehind check into guest code, keeping the regex simple.","Catch UnsupportedRegexException and use a backtracking fallback."],"exampleFix":"// before\nconst re = /(?<=.{100})token/; // trace NFA transitions > 3500\n\n// after\nconst re = /token/g; // verify offset >= 100 in JS after match","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { compile(pattern); } catch (UnsupportedRegexException e) { /* 'TraceFinder NFA transition explosion' */ return backtrackingFallback(pattern); }","preventionTips":["Narrow character classes inside lookbehind to the actual alphabet.","Avoid quantified wide classes inside lookarounds ((?<=\\p{L}{50,})).","Verify each lookaround pattern compiles in a startup smoke test."],"tags":["regex","nfa","tracefinder","transitions","lookbehind","complexity-limit","tregex"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}