caddyserver/caddy · error
evaluated placeholder %s%s%s is empty
Error message
evaluated placeholder %s%s%s is empty
What it means
In errOnEmpty mode (ReplaceOrErr with errorOnEmpty), the replacer treats a placeholder that resolves to an empty string as a failure and returns this error naming the placeholder. This is stricter than unknown-key handling: the key was found, but its value is empty, which strict consumers (like dial addresses) consider invalid.
Source
Thrown at replacer.go:269
}
// apply any transformations
if f != nil {
var err error
val, err = f(key, val)
if err != nil {
return "", err
}
}
// convert val to a string as efficiently as possible
valStr := ToString(val)
// write the value; if it's empty, either return
// an error or write a default value
if valStr == "" {
if errOnEmpty {
return "", fmt.Errorf("evaluated placeholder %s%s%s is empty",
string(phOpen), key, string(phClose))
} else if empty != "" {
sb.WriteString(empty)
}
} else {
sb.WriteString(valStr)
}
// advance cursor to end of placeholder
i = end
lastWriteCursor = i + 1
}
// flush any unwritten remainder
sb.WriteString(input[lastWriteCursor:])
return sb.String(), nil
}View on GitHub (pinned to 50e54ee279)
Solutions
- Give the variable a non-empty value, or use Caddy's placeholder default syntax {$VAR:fallback}
- Unset the variable entirely if the placeholder should be removed rather than blank
- Move the value out of a strict substituted field if empty is legitimately acceptable
Example fix
# before
output net {$LOG_ENDPOINT} # LOG_ENDPOINT=""
# after
output net {$LOG_ENDPOINT:127.0.0.1:514} Defensive patterns
Strategy: validation
Validate before calling
if v := os.Getenv("LOG_ENDPOINT"); v == "" {
return fmt.Errorf("LOG_ENDPOINT resolves to empty")
} Try / catch
if out, err := repl.ReplaceOrErr(in, true, true); err != nil {
if strings.HasPrefix(err.Error(), "evaluated placeholder") && strings.HasSuffix(err.Error(), "is empty") {
// supply a default and retry
}
} Prevention
- Use {$VAR:fallback} defaults in strict fields
- Fail fast at startup on blank env vars rather than at substitution
When it happens
Trigger: ReplaceOrErr(input, true, true) where a known placeholder expands to "" — e.g. {$EMPTY_VAR} where EMPTY_VAR is exported but set to nothing, feeding output net's address.
Common situations: Environment variables defined but blank (trailing whitespace, empty assignments in .env files); default values not configured for optional variables used in strict config fields.
Related errors
- replacing listen address: %v
- server %s, listener %d: %v
- %s: parsing listen address '%s': %v
- too many unclosed placeholders
- unrecognized placeholder %s%s%s
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/420079dc4a83acf3.
Report an issue: GitHub.