caddyserver/caddy · error
mapping %d has %d outputs but there are %d destinations defi
Error message
mapping %d has %d outputs but there are %d destinations defined
What it means
Validate in the map handler enforces 1:1 correspondence between each mapping's `outputs` and the top-level `destinations`: every mapping must have exactly len(destinations) outputs. The message reports the mapping index, its output count, and the destination count.
Source
Thrown at modules/caddyhttp/map/map.go:117
// prevent confusing/ambiguous mappings
if m.Input != "" && m.InputRegexp != "" {
return fmt.Errorf("mapping %d has both input and input_regexp fields specified, which is confusing", i)
}
// prevent duplicate mappings
input := m.Input
if m.InputRegexp != "" {
input = m.InputRegexp
}
if prev, ok := seen[input]; ok {
return fmt.Errorf("mapping %d has a duplicate input '%s' previously used with mapping %d", i, input, prev)
}
seen[input] = i
// ensure mappings have 1:1 output-to-destination correspondence
nOut := len(m.Outputs)
if nOut != nDest {
return fmt.Errorf("mapping %d has %d outputs but there are %d destinations defined", i, nOut, nDest)
}
}
return nil
}
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
// defer work until a variable is actually evaluated by using replacer's Map callback
repl.Map(func(key string) (any, bool) {
// return early if the variable is not even a configured destination
destIdx := slices.Index(h.Destinations, key)
if destIdx < 0 {
return nil, false
}
input := repl.ReplaceAll(h.Source, "")View on GitHub (pinned to 50e54ee279)
Solutions
- Add or remove outputs in the flagged mapping until they equal the number of destinations, in destination order.
- Use "-" (null) for outputs you want to skip on a given mapping.
- Recount after any change to the destinations list.
Example fix
// before (Caddyfile)
map {host} {http.vars.up} {http.vars.tier} {
a.example.com prod
}
// after
map {host} {http.vars.up} {http.vars.tier} {
a.example.com prod 1
} Defensive patterns
Strategy: validation
Validate before calling
func outputsMatchDestinations(mappings []Mapping, nDest int) bool {
for _, m := range mappings {
if len(m.Outputs) != nDest {
return false
}
}
return true
} Prevention
- Every mapping row fills every destination, in order.
- Use '-' for intentionally blank outputs.
- Re-verify all rows after adding a destination.
When it happens
Trigger: destinations has 2 entries but a mapping lists 1 or 3 outputs; Caddyfile map line `example.com foo` under a map with two destination placeholders.
Common situations: Adding a second destination ({http.vars.b}) and updating only some mappings; deleting outputs from one mapping during cleanup; misunderstanding that every row must fill every destination.
Related errors
- %d destinations != %d defaults
- destination must be a placeholder and only a placeholder
- mapping %d has both input and input_regexp fields specified,
- mapping %d has a duplicate input '%s' previously used with m
- parsing listener address: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/8a641efd96ef301b.
Report an issue: GitHub.