caddyserver/caddy · error
unrecognized placeholder %s%s%s
Error message
unrecognized placeholder %s%s%s
What it means
When the replacer runs in errOnUnknown mode (ReplaceOrErr with errorOnUnknown, used e.g. for net writer addresses and other strict contexts) and encounters a {key} that no provider in the replacer recognizes, it fails immediately with the literal placeholder in the message. Non-strict contexts would leave the placeholder untouched instead.
Source
Thrown at replacer.go:243
if nextEnd < 0 {
unclosedCount++
continue scan
}
end += nextEnd + 1
}
// write the substring from the last cursor to this point
sb.WriteString(input[lastWriteCursor:i])
// trim opening bracket
key := input[i+1 : end]
// try to get a value for this key, handle empty values accordingly
val, found := r.Get(key)
if !found {
// placeholder is unknown (unrecognized); handle accordingly
if errOnUnknown {
return "", fmt.Errorf("unrecognized placeholder %s%s%s",
string(phOpen), key, string(phClose))
} else if !treatUnknownAsEmpty {
// if treatUnknownAsEmpty is true, we'll handle an empty
// val later; so only continue otherwise
lastWriteCursor = i
continue
}
}
// apply any transformations
if f != nil {
var err error
val, err = f(key, val)
if err != nil {
return "", err
}
}
View on GitHub (pinned to 50e54ee279)
Solutions
- Fix the placeholder name or define the variable (export it, or add it to the Caddyfile env block)
- Use the correct prefix/scope: {$ENV_VAR} for environment, {http.request...} only in request-scoped contexts
- Remove the placeholder if a literal value is acceptable
Example fix
# before
output net {$SYSLOGG_HOST}:514 # typo: double G
# after
output net {$SYSLOG_HOST}:514 Defensive patterns
Strategy: validation
Validate before calling
if !repl.KnownReplacement(key) { /* caddy.Replacer: probe first */
return fmt.Errorf("placeholder %s is not defined at this stage", key)
} Try / catch
if out, err := repl.ReplaceOrErr(in, true, true); err != nil {
if strings.HasPrefix(err.Error(), "unrecognized placeholder") {
// fix or define the placeholder, then retry
}
return out, err
} Prevention
- Use {$VAR} for env vars and request-scoped names only at request time
- Prefer {$VAR:default} so substitution never depends on unset state
When it happens
Trigger: Calling replacer.ReplaceOrErr(input, true, true) where input contains an unknown placeholder like {not_a_var}; or config fields that Caddy substitutes strictly (e.g. output net address) referencing undefined env vars or placeholders.
Common situations: Typos in placeholder names ({env.FOO} vs {$FOO} confusion); env vars not present in the service environment; using HTTP placeholder names at provisioning time when they only exist at request time.
Related errors
- replacing listen address: %v
- %s: route %d, matcher set %d, matcher %d, host matcher %d: %
- too many unclosed placeholders
- evaluated placeholder %s%s%s is empty
- unknown object ID '%s'
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/6d2ae2437f7555d4.
Report an issue: GitHub.