{"record":{"id":"c0a1430287f9e174","repo":"oracle/graal","slug":"too-many-quantifiers","errorCode":null,"errorMessage":"too many quantifiers","messagePattern":"too many quantifiers","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/nfa/ASTStepVisitor.java","lineNumber":162,"sourceCode":"            setMatchedConditionGroups(stepCur.getMatchedConditionGroups());\n            root = (Term) stepCur.getRoot();\n            run(root);\n        }\n        return stepRoot;\n    }\n\n    @Override\n    protected void visit(RegexASTNode target) {\n        assert allGuardsAllowedInDFA(getTransitionGuardsOnPath());\n        ASTSuccessor successor = new ASTSuccessor();\n        long[] guards = getTransitionGuardsOnPath();\n\n        operationsBuilder.clear();\n        constraintsBuilder.clear();\n        int id = needsMaintainGuard();\n        if (id != -1) {\n            if (id > Short.MAX_VALUE) {\n                throw new UnsupportedRegexException(\"too many quantifiers\");\n            }\n            operationsBuilder.add(TransitionOp.create(id, 0, 0, TransitionOp.maintain));\n        }\n\n        for (long guard : guards) {\n            switch (TransitionGuard.getKind(guard)) {\n                case countLtMax -> constraintsBuilder.add(TransitionConstraint.create(getQuantifierIndex(guard), 0, TransitionConstraint.anyLtMax));\n                case countGeMin -> constraintsBuilder.add(TransitionConstraint.create(getQuantifierIndex(guard), 0, TransitionConstraint.anyGeMin));\n                case countLtMin -> constraintsBuilder.add(TransitionConstraint.create(getQuantifierIndex(guard), 0, TransitionConstraint.anyLtMin));\n                case countInc -> operationsBuilder.add(TransitionOp.create(getQuantifierIndex(guard), 0, 0, TransitionOp.inc));\n                case countSet1 -> operationsBuilder.add(TransitionOp.create(getQuantifierIndex(guard), 0, 0, TransitionOp.set1));\n            }\n        }\n\n        ASTTransition transition = new ASTTransition(ast.getLanguage(), constraintsBuilder.toArray(), operationsBuilder.toArray());\n        transition.setGroupBoundaries(getGroupBoundaries());\n        TBitSet matchedConditionGroups = getCurrentMatchedConditionGroups();\n        transition.setMatchedConditionGroups(matchedConditionGroups);","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/nfa/ASTStepVisitor.java#L144-L180","documentation":"Thrown in ASTStepVisitor.visit when the quantifier index returned by needsMaintainGuard() exceeds Short.MAX_VALUE (32767). Quantifier counters are indexed by a short in the compact TransitionGuard/TransitionOp encoding, so a regex with more than 32767 distinct tracked quantifiers cannot be represented. The guard id is also used to emit a 'maintain' transition op for loop quantifiers on the current path.","triggerScenarios":"Compiling a pattern that declares more than 32767 counted/bounded quantifiers (e.g. machine-generated regex with tens of thousands of a{1,2}-style terms), so the id assigned to the maintained quantifier no longer fits in a short.","commonSituations":"Programmatically generated regexes from data (token alternations, fuzzy-match expansions, generated test patterns); converting a large grammar or wildcard expression into one regex.","solutions":["Reduce the number of quantified sub-expressions below 32768 by factoring repeated parts into loops or character classes.","Split the pattern into several smaller regexes and combine matches in code.","If the pattern is generated, audit the generator for accidental per-character quantifiers (e.g. emitting x? for every optional token)."],"exampleFix":"// before\nStringBuilder sb = new StringBuilder();\nfor (String w : words) sb.append(Pattern.quote(w)).append(\"{0,1}\"); // thousands of quantifiers\n\n// after\nString alt = words.stream().map(Pattern::quote).collect(Collectors.joining(\"|\"));\nString pattern = \"(?:\" + alt + \")\"; // one alternation, no per-word quantifiers","handlingStrategy":"validation","validationCode":"int quantifiers = 0; boolean esc = false; for (char c : pattern.toCharArray()) { if (esc) { esc = false; continue; } if (c == '\\\\') { esc = true; } else if (c == '{' || c == '*' || c == '+' || c == '?') quantifiers++; } if (quantifiers > 32_000) throw new IllegalArgumentException(\"too many quantifiers for TRegex (max 32767)\");","typeGuard":"null","tryCatchPattern":"try { compile(pattern); } catch (UnsupportedRegexException e) { if (e.getMessage().contains(\"too many quantifiers\")) { /* split pattern or reject */ } }","preventionTips":["Audit regex generators for per-token quantifier emission.","Count quantifier metacharacters before compiling generated patterns.","Split large generated patterns into batches."],"tags":["regex","tregex","quantifiers","limit-exceeded","compilation"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}