gorilla/mux · critical
route %s contains capture groups in its regexp. Only non-cap
Error message
route %s contains capture groups in its regexp. Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)
What it means
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.
Source
Thrown at regexp.go:139
// Add the default pattern if the query value is empty
if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
pattern.WriteString(defaultPattern)
}
}
if typ != regexpTypePrefix {
pattern.WriteByte('$')
}
// Compile full regexp.
patternStr := pattern.String()
reg, errCompile := RegexpCompileFunc(patternStr)
if errCompile != nil {
return nil, errCompile
}
// Check for capturing groups which used to work in older versions
if reg.NumSubexp() != len(idxs)/2 {
panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) +
"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)")
}
var wildcardHostPort bool
if typ == regexpTypeHost {
if !strings.Contains(patternStr, ":") {
wildcardHostPort = true
}
}
reverse.WriteString(raw)
if endSlash {
reverse.WriteByte('/')
}
// Done!
return &routeRegexp{
template: template,
regexpType: typ,View on GitHub (pinned to db9d1d0073)
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.
Example fix
// before
r.Path("/{x:(a|b)}")
// panic: route /{x:(a|b)} contains capture groups...
// after
r.Path("/{x:(?:a|b)}") Defensive patterns
Strategy: validation
Validate before calling
func noCaptureGroups(p string) error {
re, err := regexp.Compile(p)
if err != nil { return err }
if re.NumSubexp() > 0 {
return fmt.Errorf("pattern %q has %d capture groups; use (?:...)", p, re.NumSubexp())
}
return nil
} Type guard
func safePattern(p string) bool {
re, err := regexp.Compile(p)
return err == nil && re.NumSubexp() == 0
} Try / catch
// Go has no try/catch; recover a panic at the construction boundary
defer func() {
if r := recover(); r != nil {
log.Printf("route build panicked: %v", r)
}
}()
rt := r.Path(tpl) Prevention
- 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.
When it happens
Trigger: 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 (?:...).
Common situations: 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.
Related errors
- mux: duplicated route variable %q
- mux: missing name or pattern in %q
- mux: error compiling regex for %q: %w
- mux: number of parameters must be multiple of 2, got %v
- mux: route already has name %q, can't set %q
AI-assisted analysis of gorilla/mux@db9d1d0073 (2026-08-04).
Data as JSON: /data/errors/eed73b8335a6e1e0.json.
Report an issue: GitHub.