{"record":{"id":"606b46892a2b4876","repo":"alibaba/nacos","slug":"the-number-of-capturing-groups-in-the-pattern-segm","errorCode":null,"errorMessage":"The number of capturing groups in the pattern segment {pattern} does not match the number of URI template variables it defines, which can occur if capturing groups are used in a URI template regex. Use non-capturing groups instead.","messagePattern":"The number of capturing groups in the pattern segment (.+?) does not match the number of URI template variables it defines, which can occur if capturing groups are used in a URI template regex\\. Use non-capturing groups instead\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/com/alibaba/nacos/common/packagescan/resource/AntPathMatcher.java","lineNumber":598,"sourceCode":"                return \"\";\n            }\n            return Pattern.quote(s.substring(start, end));\n        }\n\n        /**\n         * Main entry point.\n         *\n         * @return {@code true} if the string matches against the pattern, or {@code false} otherwise.\n         */\n        public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {\n            if (this.exactMatch) {\n                return this.caseSensitive ? this.rawPattern.equals(str) : this.rawPattern.equalsIgnoreCase(str);\n            } else if (this.pattern != null) {\n                Matcher matcher = this.pattern.matcher(str);\n                if (matcher.matches()) {\n                    if (uriTemplateVariables != null) {\n                        if (this.variableNames.size() != matcher.groupCount()) {\n                            throw new IllegalArgumentException(\"The number of capturing groups in the pattern segment \"\n                                    + this.pattern + \" does not match the number of URI template variables it defines, \"\n                                    + \"which can occur if capturing groups are used in a URI template regex. \"\n                                    + \"Use non-capturing groups instead.\");\n                        }\n                        for (int i = 1; i <= matcher.groupCount(); i++) {\n                            String name = this.variableNames.get(i - 1);\n                            if (name.startsWith(\"*\")) {\n                                throw new IllegalArgumentException(\"Capturing patterns (\" + name + \") are not \"\n                                        + \"supported by the AntPathMatcher. Use the PathPatternParser instead.\");\n                            }\n                            String value = matcher.group(i);\n                            uriTemplateVariables.put(name, value);\n                        }\n                    }\n                    return true;\n                }\n            }\n            return false;","sourceCodeStart":580,"sourceCodeEnd":616,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/common/src/main/java/com/alibaba/nacos/common/packagescan/resource/AntPathMatcher.java#L580-L616","documentation":"AntPathMatcher throws this when matching a URI-template pattern whose compiled regex has a different number of capturing groups than the count of named variables it extracted from the template. Each `{var}` placeholder becomes one variable and is expected to correspond to exactly one capturing group. Introducing extra capturing groups (e.g. `(a|b)` inside a regex segment) desynchronizes the two counts, so the matcher refuses to bind values rather than silently assign them to the wrong variable.","triggerScenarios":"Calling AntPathMatcher.doMatch (or extractUriTemplateVariables) with a pattern string that embeds a custom regex containing capturing parentheses, e.g. `/api/{version:[0-9]+(\\\\.[0-9]+)}` where the inner `(...)` adds an extra group beyond the one `{version}` implies. The error fires only on the matching branch that populates uriTemplateVariables (this.pattern != null and exactMatch is false).","commonSituations":"Migrating Spring URL patterns into Nacos's internal AntPathMatcher (a vendored Spring copy). Hand-writing path patterns with alternation or grouping. Copying regex from an external source that uses unescaped capturing groups. Confusing Spring's PathPatternParser syntax (which allows groups) with the stricter AntPathMatcher contract.","solutions":["Replace every capturing group `( ... )` inside the regex segment with a non-capturing group `(?: ... )` so the group count stays equal to the number of `{var}` placeholders.","Count the `{var}` placeholders in your pattern and confirm that number equals the capturing-group count of the compiled regex; reduce groups until they match.","If you need richer capture semantics, switch off AntPathMatcher and use PathPatternParser instead (as the message suggests).","Unit-test the pattern with extractUriTemplateVariables against a sample path before deploying it to a config/mapping table."],"exampleFix":"// before\nString pattern = \"/api/{version:[0-9]+(\\\\.[0-9]+)}/items\";\n// the inner (...) is a capturing group, adding one beyond {version}\n\n// after\nString pattern = \"/api/{version:[0-9]+(?:\\\\.[0-9]+)}/items\";\n// non-capturing group keeps groupCount == 1 == variableNames.size()","handlingStrategy":"validation","validationCode":"// Validate before matching: count placeholders == capturing groups\nstatic void assertPatternBalanced(String pattern) {\n    int placeholders = 0;\n    boolean inRegex = false;\n    for (int i = 0; i < pattern.length(); i++) {\n        char c = pattern.charAt(i);\n        if (c == '{') { placeholders++; inRegex = true; }\n        else if (c == '}') { inRegex = false; }\n    }\n    // recompile the regex portions and assert groupCount == placeholders per segment\n    // reject patterns with unbalanced capturing groups early\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always prefer non-capturing groups (?:...) in URI-template regex segments.","Keep a unit test that runs extractUriTemplateVariables on every registered pattern with a sample path.","Document which matcher dialect (AntPathMatcher vs PathPatternParser) each pattern is written for."],"tags":["antpathmatcher","uri-template","regex","resource-matching","validation"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}