caddyserver/caddy · error
replacing listen address: %v
Error message
replacing listen address: %v
What it means
parseAdminListenAddr applies Caddy's placeholder replacer (e.g. {$ENV} and {file...} placeholders) to the admin endpoint's listen address before parsing it. ReplaceOrErr is called with error-on-empty and error-on-missing, so any placeholder in the admin address that is unresolvable, unset, or expands to empty aborts startup with 'replacing listen address'. The wrapped %v is the replacer's underlying error naming the offending placeholder.
Source
Thrown at admin.go:1394
type APIError struct {
HTTPStatus int `json:"-"`
Err error `json:"-"`
Message string `json:"error"`
}
func (e APIError) Error() string {
if e.Err != nil {
return e.Err.Error()
}
return e.Message
}
// parseAdminListenAddr extracts a singular listen address from either addr
// or defaultAddr, returning the network and the address of the listener.
func parseAdminListenAddr(addr string, defaultAddr string) (NetworkAddress, error) {
input, err := NewReplacer().ReplaceOrErr(addr, true, true)
if err != nil {
return NetworkAddress{}, fmt.Errorf("replacing listen address: %v", err)
}
if input == "" {
input = defaultAddr
}
listenAddr, err := ParseNetworkAddress(input)
if err != nil {
return NetworkAddress{}, fmt.Errorf("parsing listener address: %v", err)
}
if listenAddr.PortRangeSize() != 1 {
return NetworkAddress{}, fmt.Errorf("must be exactly one listener address; cannot listen on: %s", listenAddr)
}
return listenAddr, nil
}
// decodeBase64DERCert base64-decodes, then DER-decodes, certStr.
func decodeBase64DERCert(certStr string) (*x509.Certificate, error) {
derBytes, err := base64.StdEncoding.DecodeString(certStr)
if err != nil {View on GitHub (pinned to 50e54ee279)
Solutions
- Check the wrapped error text — it names exactly which placeholder failed to resolve.
- Export/define the missing environment variable (or create the referenced file) before starting Caddy.
- Fix the placeholder spelling/syntax (e.g. {$CADDY_ADMIN} with matching braces) or remove it and hard-code the admin address.
- If the variable is legitimately empty in some deployments, provide a default: {$CADDY_ADMIN:localhost:2019}.
Example fix
// Caddyfile - before
admin {$CADDY_ADMIN}:2020
// after (inline default so empty/unset never fails)
admin {$CADDY_ADMIN:localhost:2019} Defensive patterns
Strategy: validation
Validate before calling
// Before loading config, verify every placeholder in admin.listen resolves.
addr := cfg.Admin.Listen // e.g. "{$CADDY_ADMIN}:2020"
if _, err := caddy.NewReplacer().ReplaceOrErr(addr, true, true); err != nil {
log.Fatalf("admin listen placeholder will fail: %v", err)
} Prevention
- Prefer inline placeholder defaults in configs: {$VAR:localhost:2019}.
- Export all referenced env vars in the unit file/container before ExecStart.
- Run 'caddy validate --config' in CI with the same environment as production.
When it happens
Trigger: Setting 'admin {$CADDY_ADMIN}:2020' (or admin_listen/offline addressing via JSON "admin":{"listen":"..."}) where the referenced environment variable or file placeholder does not exist, is empty, or is malformed (e.g. unclosed '{$'). Any admin API config load or 'caddy run' whose Admin.Listen contains an unresolvable placeholder hits this in parseAdminListenAddr.
Common situations: Running Caddy under systemd/container where an env var referenced in the admin block is not exported; typos like {$ADMIM_ADDR}; migrating configs between environments where the variable was previously set; using {file./path} where the file is missing.
Related errors
- unknown object ID '%s'
- decoding request body: %w, at offset %d
- decoding request body: %w
- no traversable path
- path missing
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/a696d0d13daa23f4.
Report an issue: GitHub.