gorilla/mux · error

mux: unbalanced braces in %q

Error message

mux: unbalanced braces in %q

What it means

Returned by braceIndices() (regexp.go:312) when scanning a route template and a '}' is encountered while the brace nesting level is already 0 — i.e. a closing brace with no matching opening brace earlier in the string. newRouteRegexp propagates this when registering the route, so it surfaces at Router setup time, not request time.

Source

Thrown at regexp.go:312

	return r.regexp.MatchString(r.getURLQuery(req))
}

// braceIndices returns the first level curly brace indices from a string.
// It returns an error in case of unbalanced braces.
func braceIndices(s string) ([]int, error) {
	var level, idx int
	var idxs []int
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '{':
			if level++; level == 1 {
				idx = i
			}
		case '}':
			if level--; level == 0 {
				idxs = append(idxs, idx, i+1)
			} else if level < 0 {
				return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
			}
		}
	}
	if level != 0 {
		return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
	}
	return idxs, nil
}

// varGroupName builds a capturing group name for the indexed variable.
func varGroupName(idx int) string {
	return "v" + strconv.Itoa(idx)
}

// ----------------------------------------------------------------------------
// routeRegexpGroup
// ----------------------------------------------------------------------------

View on GitHub (pinned to db9d1d0073)

Solutions

  1. Locate the stray '}' reported by the offending template (the %q) and remove it or balance it with a matching '{'.
  2. If you need a regexp bounded quantifier like {2,4}, keep it entirely inside a variable's pattern segment, e.g. {id:[0-9]{2,4}}, so the outer brace pair still balances.
  3. For a genuinely literal brace in the path, escape it within the variable pattern (e.g. {lit:\}\{foo}) rather than leaving it bare.
  4. Register routes in a test (router.Walk / a smoke Handler request) so the failure is caught at startup, not in production boot.

Example fix

// before: stray closing brace
r.NewRoute().Path("/foo}bar")
// -> mux: unbalanced braces in "/foo}bar"

// after
r.NewRoute().Path("/foobar")

// if you wanted a bounded digit quantifier, keep braces paired inside the var pattern
r.NewRoute().Path("/items/{id:[0-9]{2,4}}")
Defensive patterns

Strategy: validation

Validate before calling

// Reject stray '}' (closing brace with no matching '{') before registering a route.
func balancedBraces(tpl string) error {
    level := 0
    for i := 0; i < len(tpl); i++ {
        switch tpl[i] {
        case '{':
            level++
        case '}':
            level--
            if level < 0 {
                return fmt.Errorf("stray '}' at offset %d in %q", i, tpl)
            }
        }
    }
    if level != 0 {
        return fmt.Errorf("unbalanced '{' in %q", tpl)
    }
    return nil
}

if err := balancedBraces(tpl); err != nil { return err }
r.NewRoute().Path(tpl)

Try / catch

// Registration-time: build the router in a function that returns an error.
router, err := buildRouter()
if err != nil {
    // err.Error() is: mux: unbalanced braces in %q
    log.Fatalf("router setup failed: %v", err)
}

Prevention

When it happens

Trigger: r.NewRoute().Path("/foo}bar"), .Host("api}.example.com"), or .Queries("k", "{v}")-style templates containing a literal '}' that isn't part of a {...} variable. Also triggered by regexp quantifier braces such as /items/{id:[0-9]{2,4}} where the inner {2,4} is misread as a variable delimiter.

Common situations: Copy-pasting a URL that legitimately contains a brace; attempting an inline bounded quantifier inside a variable pattern without realising mux parses top-level braces; truncating or hand-editing a template and dropping the opening '{'.

Related errors


AI-assisted analysis of gorilla/mux@db9d1d0073 (2026-08-04). Data as JSON: /data/errors/2bb7ebf31337300c.json. Report an issue: GitHub.