{"id":"eed73b8335a6e1e0","repo":"gorilla/mux","slug":"route-s-contains-capture-groups-in-its-regexp-on","errorCode":null,"errorMessage":"route %s contains capture groups in its regexp. Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)","messagePattern":"route (.+?) contains capture groups in its regexp\\. Only non-capturing groups are accepted: e\\.g\\. \\(\\?:pattern\\) instead of \\(pattern\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"regexp.go","lineNumber":139,"sourceCode":"\t\t// Add the default pattern if the query value is empty\n\t\tif queryVal := strings.SplitN(template, \"=\", 2)[1]; queryVal == \"\" {\n\t\t\tpattern.WriteString(defaultPattern)\n\t\t}\n\t}\n\tif typ != regexpTypePrefix {\n\t\tpattern.WriteByte('$')\n\t}\n\n\t// Compile full regexp.\n\tpatternStr := pattern.String()\n\treg, errCompile := RegexpCompileFunc(patternStr)\n\tif errCompile != nil {\n\t\treturn nil, errCompile\n\t}\n\n\t// Check for capturing groups which used to work in older versions\n\tif reg.NumSubexp() != len(idxs)/2 {\n\t\tpanic(fmt.Sprintf(\"route %s contains capture groups in its regexp. \", template) +\n\t\t\t\"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)\")\n\t}\n\n\tvar wildcardHostPort bool\n\tif typ == regexpTypeHost {\n\t\tif !strings.Contains(patternStr, \":\") {\n\t\t\twildcardHostPort = true\n\t\t}\n\t}\n\treverse.WriteString(raw)\n\tif endSlash {\n\t\treverse.WriteByte('/')\n\t}\n\n\t// Done!\n\treturn &routeRegexp{\n\t\ttemplate:         template,\n\t\tregexpType:       typ,","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/gorilla/mux/blob/db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265/regexp.go#L121-L157","documentation":"This is a PANIC (regexp.go:138-141), not a returned error. After compiling the full route regexp, the library checks reg.NumSubexp() equals the number of declared variables; if not, it panics because the user's pattern introduced extra capturing groups (e.g. (a|b)), which mux forbids. Only named groups for variables and non-capturing groups (?:...) are allowed. The panic occurs during route construction (Path/Host/PathPrefix/Queries), crashing the program at startup unless recovered.","triggerScenarios":"r.Path(\"/{x:(a|b)}\") — the inner (a|b) is a capturing group adding a subexpression beyond the named {x} group. Any bare (...) inside a variable pattern that is not (?:...).","commonSituations":"Porting regexes from languages whose parens capture by default; writing {lang:(en|fr|de)} instead of {lang:(?:en|fr|de)}; forgetting RE2 still creates capture groups for bare parens.","solutions":["Replace every capturing group (...) inside a variable pattern with a non-capturing group (?:...).","Re-run; the panic disappears once NumSubexp matches the variable count.","If patterns come from untrusted input, wrap route construction in a func with defer recover() and log the offending template."],"exampleFix":"// before\nr.Path(\"/{x:(a|b)}\")\n// panic: route /{x:(a|b)} contains capture groups...\n\n// after\nr.Path(\"/{x:(?:a|b)}\")","handlingStrategy":"validation","validationCode":"func noCaptureGroups(p string) error {\n    re, err := regexp.Compile(p)\n    if err != nil { return err }\n    if re.NumSubexp() > 0 {\n        return fmt.Errorf(\"pattern %q has %d capture groups; use (?:...)\", p, re.NumSubexp())\n    }\n    return nil\n}","typeGuard":"func safePattern(p string) bool {\n    re, err := regexp.Compile(p)\n    return err == nil && re.NumSubexp() == 0\n}","tryCatchPattern":"// Go has no try/catch; recover a panic at the construction boundary\ndefer func() {\n    if r := recover(); r != nil {\n        log.Printf(\"route build panicked: %v\", r)\n    }\n}()\nrt := r.Path(tpl)","preventionTips":["Always use (?:...) for grouping inside variable patterns.","Pre-validate patterns with NumSubexp()==0 before registering.","Wrap user-driven route construction with recover().","Unit-test route registration at startup so panics surface in CI."],"tags":["routing","config","regexp","panic","build-time"],"analyzedSha":"db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265","analyzedAt":"2026-08-04T21:35:47.097Z","schemaVersion":2}