{"record":{"id":"89db39f35108e14c","repo":"caddyserver/caddy","slug":"mapping-d-has-a-duplicate-input-s-previously-u","errorCode":null,"errorMessage":"mapping %d has a duplicate input '%s' previously used with mapping %d","messagePattern":"mapping (.+?) has a duplicate input '(.+?)' previously used with mapping (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/map/map.go","lineNumber":110,"sourceCode":"\tnDest, nDef := len(h.Destinations), len(h.Defaults)\n\tif nDef > 0 && nDef != nDest {\n\t\treturn fmt.Errorf(\"%d destinations != %d defaults\", nDest, nDef)\n\t}\n\n\tseen := make(map[string]int)\n\tfor i, m := range h.Mappings {\n\t\t// prevent confusing/ambiguous mappings\n\t\tif m.Input != \"\" && m.InputRegexp != \"\" {\n\t\t\treturn fmt.Errorf(\"mapping %d has both input and input_regexp fields specified, which is confusing\", i)\n\t\t}\n\n\t\t// prevent duplicate mappings\n\t\tinput := m.Input\n\t\tif m.InputRegexp != \"\" {\n\t\t\tinput = m.InputRegexp\n\t\t}\n\t\tif prev, ok := seen[input]; ok {\n\t\t\treturn fmt.Errorf(\"mapping %d has a duplicate input '%s' previously used with mapping %d\", i, input, prev)\n\t\t}\n\t\tseen[input] = i\n\n\t\t// ensure mappings have 1:1 output-to-destination correspondence\n\t\tnOut := len(m.Outputs)\n\t\tif nOut != nDest {\n\t\t\treturn fmt.Errorf(\"mapping %d has %d outputs but there are %d destinations defined\", i, nOut, nDest)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {\n\trepl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)\n\n\t// defer work until a variable is actually evaluated by using replacer's Map callback\n\trepl.Map(func(key string) (any, bool) {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/map/map.go#L92-L128","documentation":"Validate in the map handler tracks every mapping's input (exact or regexp) in a `seen` map and rejects duplicates, because later duplicates would shadow earlier ones. The message names the duplicate input and both mapping indices.","triggerScenarios":"Two mappings with input \"foo\", or one mapping input \"foo\" and another input_regexp \"foo\" (regexp inputs are compared by their pattern string).","commonSituations":"Large map blocks maintained by several people; merging map fragments from different sites; patterns that differ only in whitespace so they look distinct but hash the same after formatting.","solutions":["Remove the later duplicate mapping named in the error.","If the duplicate was intentional (e.g. same input, different outputs), merge the outputs into one mapping.","Lint generated map configs for duplicate keys before deploy."],"exampleFix":"// before (Caddyfile)\nmap {host} {http.vars.up} {\n  a.example.com app1\n  a.example.com app2\n}\n\n// after\nmap {host} {http.vars.up} {\n  a.example.com app1\n}","handlingStrategy":"validation","validationCode":"func noDuplicateInputs(mappings []Mapping) bool {\n\tseen := map[string]bool{}\n\tfor _, m := range mappings {\n\t\tk := m.Input\n\t\tif m.InputRegexp != \"\" {\n\t\t\tk = m.InputRegexp\n\t\t}\n\t\tif seen[k] {\n\t\t\treturn false\n\t\t}\n\t\tseen[k] = true\n\t}\n\treturn true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Deduplicate map inputs when generating configs.","Remember regexp patterns compare by their literal pattern string."],"tags":["caddy","config","map-handler","duplicates","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}