caddyserver/caddy · error
must be exactly one listener address; cannot listen on: %s
Error message
must be exactly one listener address; cannot listen on: %s
What it means
The admin API binds exactly one listener, so parseAdminListenAddr rejects any address whose PortRangeSize() != 1. Addresses like 'localhost:2020-2025' or a hostname that expands to multiple ports parse fine but describe a port range, and this error names the offending address. It fires before any socket is opened.
Source
Thrown at admin.go:1404
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 {
return nil, err
}
return x509.ParseCertificate(derBytes)
}
type loggableURLArray []*url.URL
func (ua loggableURLArray) MarshalLogArray(enc zapcore.ArrayEncoder) error {
if ua == nil {
return nilView on GitHub (pinned to 50e54ee279)
Solutions
- Change the admin listen address to a single port: 'admin localhost:2019'.
- If you need multiple admin surfaces, run separate Caddy instances rather than a port range.
- Double-check for accidental dashes or commas in the port field.
Example fix
// before admin localhost:2020-2025 // after admin localhost:2019
Defensive patterns
Strategy: validation
Validate before calling
na, err := caddy.ParseNetworkAddress(adminAddr)
if err != nil {
return err
}
if na.PortRangeSize() != 1 {
return fmt.Errorf("admin needs exactly one port, got %d in %s", na.PortRangeSize(), adminAddr)
} Prevention
- Never carry site-style port ranges into the admin directive.
- Treat the admin endpoint as a single singleton listener in architecture reviews.
When it happens
Trigger: Setting 'admin localhost:2020-2025' in a Caddyfile or "admin":{"listen":"localhost:2020-2025"} in JSON; any admin listen value with a port range or wildcard port expansion that yields more than one port.
Common situations: Copy-pasting site-listen patterns (port ranges are valid for HTTP apps) into the admin block; attempting load-balanced admin across a port range; misunderstanding that the admin endpoint is a singular, unique listener.
Related errors
- parsing listener address: %v
- indexing config: %v
- %s: %s field must be a string or number
- duplicate ID '%s' found at %s and %s
- server %s: listener address repeated: %s (already claimed by
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/e39ff00eddb9fce8.
Report an issue: GitHub.