{"record":{"id":"0818d53e6702f281","repo":"caddyserver/caddy","slug":"destination-must-be-a-placeholder-and-only-a-place","errorCode":null,"errorMessage":"destination must be a placeholder and only a placeholder","messagePattern":"destination must be a placeholder and only a placeholder","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/map/map.go","lineNumber":68,"sourceCode":"\n\t// If no mappings match or if the mapped output is null/nil, the associated\n\t// default output will be applied (optional).\n\tDefaults []string `json:\"defaults,omitempty\"`\n}\n\n// CaddyModule returns the Caddy module information.\nfunc (Handler) CaddyModule() caddy.ModuleInfo {\n\treturn caddy.ModuleInfo{\n\t\tID:  \"http.handlers.map\",\n\t\tNew: func() caddy.Module { return new(Handler) },\n\t}\n}\n\n// Provision sets up h.\nfunc (h *Handler) Provision(_ caddy.Context) error {\n\tfor j, dest := range h.Destinations {\n\t\tif strings.Count(dest, \"{\") != 1 || !strings.HasPrefix(dest, \"{\") {\n\t\t\treturn fmt.Errorf(\"destination must be a placeholder and only a placeholder\")\n\t\t}\n\t\th.Destinations[j] = strings.Trim(dest, \"{}\")\n\t}\n\n\tfor i, m := range h.Mappings {\n\t\tif m.InputRegexp == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tvar err error\n\t\th.Mappings[i].re, err = regexp.Compile(m.InputRegexp)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"compiling regexp for mapping %d: %v\", i, err)\n\t\t}\n\t}\n\n\t// TODO: improve efficiency even further by using an actual map type\n\t// for the non-regexp mappings, OR sort them and do a binary search\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/map/map.go#L50-L86","documentation":"The `map` handler (http.handlers.map) requires each entry of `destinations` to be exactly one placeholder token such as {http.vars.my_var}. Provision rejects a destination whose '{' count is not exactly 1 or that does not start with '{', because the trimmed name is later used as a replacer variable key.","triggerScenarios":"JSON/Caddyfile map config with destinations: [\"my_var\"], [\"{a}{b}\"], [\"literal\"], or a placeholder missing its closing brace like \"{http.vars.foo\".","commonSituations":"Assuming destinations are plain variable names (common when moving from rewrite/set patterns); nesting placeholders; leftover braces from templating; adapting an nginx map block where the variable has no braces.","solutions":["Wrap the destination in a single placeholder: {http.vars.my_var}.","Remove any extra braces or literals from the destination entry.","One destination per output; do not concatenate two placeholders into one entry."],"exampleFix":"// before (Caddyfile)\nmap {http.request.host} {dest} {\n  example.com foo\n}\n\n// after\nmap {http.request.host} {http.vars.dest} {\n  example.com foo\n}","handlingStrategy":"validation","validationCode":"import \"regexp\"\n\nvar destRE = regexp.MustCompile(`^\\{[^{}]+\\}$`)\n\nfunc validDestinations(dests []string) bool {\n\tfor _, d := range dests {\n\t\tif !destRE.MatchString(d) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use {http.vars.name} form for map destinations.","Exactly one placeholder per destination entry.","Adapt from Caddyfile syntax which makes the brace form natural."],"tags":["caddy","config","map-handler","placeholder","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}