{"id":"5943f76607a86dd4","repo":"gorilla/mux","slug":"mux-missing-route-variable-q","errorCode":null,"errorMessage":"mux: missing route variable %q","messagePattern":"mux: missing route variable %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"regexp.go","lineNumber":217,"sourceCode":"\t}\n\n\tif r.regexpType == regexpTypeQuery {\n\t\treturn r.matchQueryString(req)\n\t}\n\tpath := req.URL.Path\n\tif r.options.useEncodedPath {\n\t\tpath = req.URL.EscapedPath()\n\t}\n\treturn r.regexp.MatchString(path)\n}\n\n// url builds a URL part using the given values.\nfunc (r *routeRegexp) url(values map[string]string) (string, error) {\n\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}","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/gorilla/mux/blob/db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265/regexp.go#L199-L235","documentation":"Returned by routeRegexp.url() (regexp.go:217) during reverse URL building via Route.URL/URLPath/URLHost when a named placeholder declared in the route template has no entry in the supplied pairs. gorilla/mux treats every {...} variable as required, so omitting any one aborts URL construction and surfaces the missing variable name in the message.","triggerScenarios":"Calling r.Get(\"article\").URL(\"category\", \"tech\") for a route registered as Path(\"/articles/{category}/{id:[0-9]+}\") — id is missing. Same for URLPath/URLHost when the host/path template declares more variables than the caller passes.","commonSituations":"A new {var} was added to a route template but an existing URL() call site wasn't updated; a variable was renamed; key/value pairs were passed in the wrong even/odd order so a value landed where a key was expected; refactor moved URL building into a helper that dropped a pair.","solutions":["Read the variable name in the error message (the %q) and add the corresponding \"name\", \"value\" pair to the URL/URLPath/URLHost call.","Cross-check against r.GetPathTemplate() (or GetHostTemplate) to enumerate every required variable before constructing the URL.","Wrap URL() construction in a helper that takes a map[string]string and asserts it covers GetPathTemplate()'s variables, failing loudly at startup.","If the variable is genuinely optional, split the route into two routes (one with, one without the segment) instead of trying to omit it."],"exampleFix":"// before: route is /articles/{category}/{id:[0-9]+}\nurl, err := r.Get(\"article\").URL(\"category\", \"tech\")\n// err: mux: missing route variable \"id\"\n\n// after\nurl, err := r.Get(\"article\").URL(\"category\", \"tech\", \"id\", \"42\")","handlingStrategy":"validation","validationCode":"// Validate before calling Route.URL / URLPath / URLHost\ntpl, err := r.Get(\"article\").GetPathTemplate()\nif err != nil { return err }\n\nrequired := routeVarNames(tpl) // parses {name} and {name:pat} out of tpl\nprovided := map[string]string{\"category\": cat, \"id\": id} // your pairs\nfor _, name := range required {\n    if _, ok := provided[name]; !ok {\n        return fmt.Errorf(\"missing route variable %q\", name)\n    }\n}\nreturn nil\n\n// helper\nfunc routeVarNames(tpl string) []string {\n    var out []string\n    for i := 0; i < len(tpl); i++ {\n        if tpl[i] != '{' { continue }\n        j := strings.IndexByte(tpl[i:], '}')\n        if j < 0 { break }\n        body := tpl[i+1 : i+j]\n        if c := strings.IndexByte(body, ':'); c >= 0 { body = body[:c] }\n        out = append(out, body)\n        i += j\n    }\n    return out\n}","typeGuard":null,"tryCatchPattern":"// Go: check the returned error, don't ignore it.\nu, err := r.Get(\"article\").URL(\"category\", cat, \"id\", id)\nif err != nil {\n    // err.Error() is exactly: mux: missing route variable %q\n    var name string\n    if _, gerr := fmt.Sscanf(err.Error(), \"mux: missing route variable %q\", &name); gerr == nil {\n        return fmt.Errorf(\"cannot build URL: required variable %s not supplied\", name)\n    }\n    return err\n}\nreturn u","preventionTips":["Never ignore the error returned by Route.URL / URLPath / URLHost.","Centralize URL building per route in a typed helper whose signature lists every variable, so the compiler enforces completeness.","When you add or rename a {var} in a route template, grep for every URL()/URLPath()/URLHost() call on that named route the same commit.","Add a startup test that constructs URLs for every named route with sample values so a missing variable fails CI, not production."],"tags":["routing","url-building","reverse-routing","gorilla-mux"],"analyzedSha":"db9d1d0073d27a0a2d9a8c1bc52aa0af4374d265","analyzedAt":"2026-08-04T21:35:47.097Z","schemaVersion":2}