caddyserver/caddy · error
destination must be a placeholder and only a placeholder
Error message
destination must be a placeholder and only a placeholder
What it means
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.
Source
Thrown at modules/caddyhttp/map/map.go:68
// If no mappings match or if the mapped output is null/nil, the associated
// default output will be applied (optional).
Defaults []string `json:"defaults,omitempty"`
}
// CaddyModule returns the Caddy module information.
func (Handler) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.map",
New: func() caddy.Module { return new(Handler) },
}
}
// Provision sets up h.
func (h *Handler) Provision(_ caddy.Context) error {
for j, dest := range h.Destinations {
if strings.Count(dest, "{") != 1 || !strings.HasPrefix(dest, "{") {
return fmt.Errorf("destination must be a placeholder and only a placeholder")
}
h.Destinations[j] = strings.Trim(dest, "{}")
}
for i, m := range h.Mappings {
if m.InputRegexp == "" {
continue
}
var err error
h.Mappings[i].re, err = regexp.Compile(m.InputRegexp)
if err != nil {
return fmt.Errorf("compiling regexp for mapping %d: %v", i, err)
}
}
// TODO: improve efficiency even further by using an actual map type
// for the non-regexp mappings, OR sort them and do a binary search
View on GitHub (pinned to 50e54ee279)
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.
Example fix
// before (Caddyfile)
map {http.request.host} {dest} {
example.com foo
}
// after
map {http.request.host} {http.vars.dest} {
example.com foo
} Defensive patterns
Strategy: validation
Validate before calling
import "regexp"
var destRE = regexp.MustCompile(`^\{[^{}]+\}$`)
func validDestinations(dests []string) bool {
for _, d := range dests {
if !destRE.MatchString(d) {
return false
}
}
return true
} Prevention
- 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.
When it happens
Trigger: JSON/Caddyfile map config with destinations: ["my_var"], ["{a}{b}"], ["literal"], or a placeholder missing its closing brace like "{http.vars.foo".
Common situations: 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.
Related errors
- %d destinations != %d defaults
- mapping %d has both input and input_regexp fields specified,
- mapping %d has a duplicate input '%s' previously used with m
- mapping %d has %d outputs but there are %d destinations defi
- parsing listener address: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/0818d53e6702f281.
Report an issue: GitHub.