caddyserver/caddy · error

server %s: listener protocols count does not match address c

Error message

server %s: listener protocols count does not match address count: %d != %d

What it means

Per-server validation in the http app: when listen_protocols is set, its slice length must equal the length of the listen addresses slice, because the two are index-paired. A mismatch aborts provisioning with both counts printed.

Source

Thrown at modules/caddyhttp/app.go:258

			app.Metrics.PerHost = app.Metrics.PerHost || srv.Metrics.PerHost
		}

		// only enable access logs if configured
		if srv.Logs != nil {
			srv.accessLogger = app.logger.Named("log.access")
			if srv.Logs.Trace {
				srv.traceLogger = app.logger.Named("log.trace")
			}
		}

		// if no protocols configured explicitly, enable all except h2c
		if len(srv.Protocols) == 0 {
			srv.Protocols = srv.protocolsWithDefaults()
		}

		if srv.ListenProtocols != nil {
			if len(srv.ListenProtocols) != len(srv.Listen) {
				return fmt.Errorf("server %s: listener protocols count does not match address count: %d != %d",
					srvName, len(srv.ListenProtocols), len(srv.Listen))
			}

			for i, lnProtocols := range srv.ListenProtocols {
				if lnProtocols != nil {
					srv.ListenProtocols[i] = srv.listenerProtocolsWithDefaults(lnProtocols)
				}
			}
		}

		// limit max header bytes to a more reasonable default than 1MB from Go std lib
		// (see https://github.com/php/frankenphp/issues/2459#issuecomment-4655612909)
		if srv.MaxHeaderBytes <= 0 {
			srv.MaxHeaderBytes = 16 * 1024
		}

		// if not explicitly configured by the user, disallow TLS
		// client auth bypass (domain fronting) which could

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Make the arrays the same length: one protocols entry (possibly null for defaults) per listen address, in the same order.
  2. Or drop listen_protocols entirely to use the default protocol set on all listeners.
  3. Use null (JSON) / leave an empty entry for any listener that should keep defaults.

Example fix

// before (caddyfile)
srv0 {
    listen :443 :80
    listen_protocols h1 h2 h3
}

// after
srv0 {
    listen :443 :80
    listen_protocols h1 h2 h3 h1 h2
}
Defensive patterns

Strategy: validation

Validate before calling

// before load: arrays must be index-paired
if len(srv.ListenProtocols) != 0 && len(srv.ListenProtocols) != len(srv.Listen) {
    return fmt.Errorf("listen_protocols has %d entries but listen has %d addresses", len(srv.ListenProtocols), len(srv.Listen))
}

Try / catch

if err := caddy.Validate(cfg); err != nil {
    if strings.Contains(err.Error(), "listener protocols count does not match address count") {
        // align the two arrays (or drop listen_protocols) and re-validate
    }
    return err
}

Prevention

When it happens

Trigger: server { listen [":443", ":80"] listen_protocols [["h1","h2","h3"]] } — 2 addresses but 1 protocols entry. Also triggered by JSON configs with mismatched arrays, or removing a listen address without updating protocols.

Common situations: Adding/removing a listen address and forgetting listen_protocols; hand-edited JSON; copy-paste between server blocks with different address counts.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/af4d5654076c0178. Report an issue: GitHub.