{"id":"6c91aaf0f03f5be3","repo":"gorilla/mux","slug":"mux-variable-q-doesn-t-match-expected-q","errorCode":null,"errorMessage":"mux: variable %q doesn't match, expected %q","messagePattern":"mux: variable %q doesn't match, expected %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"regexp.go","lineNumber":231,"sourceCode":"\turlValues := make([]interface{}, len(r.varsN))\n\tfor k, v := range r.varsN {\n\t\tvalue, ok := values[v]\n\t\tif !ok {\n\t\t\treturn \"\", fmt.Errorf(\"mux: missing route variable %q\", v)\n\t\t}\n\t\tif r.regexpType == regexpTypeQuery {\n\t\t\tvalue = url.QueryEscape(value)\n\t\t}\n\t\turlValues[k] = value\n\t}\n\trv := fmt.Sprintf(r.reverse, urlValues...)\n\tif !r.regexp.MatchString(rv) {\n\t\t// The URL is checked against the full regexp, instead of checking\n\t\t// individual variables. This is faster but to provide a good error\n\t\t// message, we check individual regexps if the URL doesn't match.\n\t\tfor k, v := range r.varsN {\n\t\t\tif !r.varsR[k].MatchString(values[v]) {\n\t\t\t\treturn \"\", fmt.Errorf(\n\t\t\t\t\t\"mux: variable %q doesn't match, expected %q\", values[v],\n\t\t\t\t\tr.varsR[k].String())\n\t\t\t}\n\t\t}\n\t}\n\treturn rv, nil\n}\n\n// getURLQuery returns a single query parameter from a request URL.\n// For a URL with foo=bar&baz=ding, we return only the relevant key\n// value pair for the routeRegexp.\nfunc (r *routeRegexp) getURLQuery(req *http.Request) string {\n\tif r.regexpType != regexpTypeQuery {\n\t\treturn \"\"\n\t}\n\ttemplateKey := strings.SplitN(r.template, \"=\", 2)[0]\n\tval, ok := findFirstQueryKey(req.URL.RawQuery, templateKey)\n\tif ok {","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/gorilla/mux/blob/db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265/regexp.go#L213-L249","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nurl, err := r.Get(\"article\").URL(\"id\", userInput)\n// userInput=\"abc\" -> mux: variable \"abc\" doesn't match, expected \"[0-9]+\"\n\n// after: validate against the same constraint before building\nvar idRe = regexp.MustCompile(`^[0-9]+$`)\nif !idRe.MatchString(userInput) {\n    return errors.New(\"invalid id\")\n}\nurl, err := r.Get(\"article\").URL(\"id\", userInput)","handlingStrategy":"validation","validationCode":"// Reuse the route's own constraint by compiling the pattern you declared.\n// e.g. Path(\"/articles/{id:[0-9]+}\") -> validate with the same regexp.\nvar idRe = regexp.MustCompile(`^[0-9]+$`)\n\nfunc validID(s string) bool { return idRe.MatchString(s) }\n\n// call before URL()\nif !validID(id) {\n    return fmt.Errorf(\"id %q violates [0-9]+\", id)\n}\nu, err := r.Get(\"article\").URL(\"id\", id)","typeGuard":null,"tryCatchPattern":"u, err := r.Get(\"article\").URL(\"id\", id)\nif err != nil {\n    // Surface the offending value and expected pattern to the caller,\n    // or map to HTTP 400 if the value came from a client.\n    var val, want string\n    if n, _ := fmt.Sscanf(err.Error(),\n        \"mux: variable %q doesn't match, expected %q\", &val, &want); n == 2 {\n        return fmt.Errorf(\"invalid value %q: must match %s\", val, want)\n    }\n    return err\n}","preventionTips":["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."],"tags":["routing","url-building","validation","regexp","gorilla-mux"],"analyzedSha":"db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265","analyzedAt":"2026-08-04T21:35:47.097Z","schemaVersion":2}