gorilla/mux · error
mux: variable %q doesn't match, expected %q
Error message
mux: variable %q doesn't match, expected %q
What it means
Returned by routeRegexp.url() (regexp.go:231) when reverse URL building succeeds in formatting the string but the full regexp then fails to MatchString it. mux re-checks each variable's own pattern (varsR) and reports the first value that violates its constraint, printing both the offending value and the expected regexp.
Source
Thrown at regexp.go:231
urlValues := make([]interface{}, len(r.varsN))
for k, v := range r.varsN {
value, ok := values[v]
if !ok {
return "", fmt.Errorf("mux: missing route variable %q", v)
}
if r.regexpType == regexpTypeQuery {
value = url.QueryEscape(value)
}
urlValues[k] = value
}
rv := fmt.Sprintf(r.reverse, urlValues...)
if !r.regexp.MatchString(rv) {
// The URL is checked against the full regexp, instead of checking
// individual variables. This is faster but to provide a good error
// message, we check individual regexps if the URL doesn't match.
for k, v := range r.varsN {
if !r.varsR[k].MatchString(values[v]) {
return "", fmt.Errorf(
"mux: variable %q doesn't match, expected %q", values[v],
r.varsR[k].String())
}
}
}
return rv, nil
}
// getURLQuery returns a single query parameter from a request URL.
// For a URL with foo=bar&baz=ding, we return only the relevant key
// value pair for the routeRegexp.
func (r *routeRegexp) getURLQuery(req *http.Request) string {
if r.regexpType != regexpTypeQuery {
return ""
}
templateKey := strings.SplitN(r.template, "=", 2)[0]
val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey)
if ok {View on GitHub (pinned to db9d1d0073)
Solutions
- Inspect the expected regexp in the error and validate the value with regexp.MustCompile(thatPattern).MatchString(value) before calling URL().
- If the value legitimately can be that shape, widen the route's inline pattern (e.g. {id:[0-9a-zA-Z_-]+}).
- For user-supplied IDs, reject early with HTTP 400 / a typed error rather than letting URL() fail.
- Normalize the value first (trim, lowercase, url.PathEscape only where appropriate) so it matches the declared charset.
Example fix
// before
url, err := r.Get("article").URL("id", userInput)
// userInput="abc" -> mux: variable "abc" doesn't match, expected "[0-9]+"
// after: validate against the same constraint before building
var idRe = regexp.MustCompile(`^[0-9]+$`)
if !idRe.MatchString(userInput) {
return errors.New("invalid id")
}
url, err := r.Get("article").URL("id", userInput) Defensive patterns
Strategy: validation
Validate before calling
// Reuse the route's own constraint by compiling the pattern you declared.
// e.g. Path("/articles/{id:[0-9]+}") -> validate with the same regexp.
var idRe = regexp.MustCompile(`^[0-9]+$`)
func validID(s string) bool { return idRe.MatchString(s) }
// call before URL()
if !validID(id) {
return fmt.Errorf("id %q violates [0-9]+", id)
}
u, err := r.Get("article").URL("id", id) Try / catch
u, err := r.Get("article").URL("id", id)
if err != nil {
// Surface the offending value and expected pattern to the caller,
// or map to HTTP 400 if the value came from a client.
var val, want string
if n, _ := fmt.Sscanf(err.Error(),
"mux: variable %q doesn't match, expected %q", &val, &want); n == 2 {
return fmt.Errorf("invalid value %q: must match %s", val, want)
}
return err
} Prevention
- Treat the inline pattern in {name:pattern} as a contract shared by producers and the router — keep the same regexp literal in your validation helper.
- Don't forward raw user input into URL(); sanitize at the trust boundary first.
- When tightening a route's pattern, audit every caller that builds URLs to that route.
- For numeric IDs, parse with strconv.ParseInt and pass the canonicalized form, not the raw string.
When it happens
Trigger: Route declares a constraint and the caller supplies a non-conforming value, e.g. Path("/articles/{id:[0-9]+}") plus URL("id", "abc"), or Host("{sub:[a-z]+}.example.com") plus URL("sub", "News42"), or a query {q:[a-z]+} with uppercase/symbols.
Common situations: Raw user input is forwarded into URL() without sanitization; a route's inline regexp was tightened (e.g. [0-9]+ added) but producers still emit old formats; locale/unicode characters slip into an ASCII-only pattern; values containing '/' hit the default [^/]+ reversal in surprising ways.
Related errors
- mux: missing route variable %q
- mux: duplicated route variable %q
- mux: route doesn't have a host
- mux: route doesn't have a path
- mux: route does not have a path
AI-assisted analysis of gorilla/mux@db9d1d0073 (2026-08-04).
Data as JSON: /data/errors/6c91aaf0f03f5be3.json.
Report an issue: GitHub.