caddyserver/caddy · error

invalid IP address: '%s': %v

Error message

invalid IP address: '%s': %v

What it means

Companion to the CIDR branch in CIDRExpressionToPrefix (modules/caddyhttp/ip_range.go): an entry without '/' is parsed as a single IP with netip.ParseAddr; failure yields this error during module Provision. Because the function then builds a full-length prefix via PrefixFrom(addr, addr.BitLen()), the input must be a valid bare address.

Source

Thrown at modules/caddyhttp/ip_range.go:119

	return nil
}

// CIDRExpressionToPrefix takes a string which could be either a
// CIDR expression or a single IP address, and returns a netip.Prefix.
func CIDRExpressionToPrefix(expr string) (netip.Prefix, error) {
	// Having a slash means it should be a CIDR expression
	if strings.Contains(expr, "/") {
		prefix, err := netip.ParsePrefix(expr)
		if err != nil {
			return netip.Prefix{}, fmt.Errorf("parsing CIDR expression: '%s': %v", expr, err)
		}
		return prefix, nil
	}

	// Otherwise it's likely a single IP address
	parsed, err := netip.ParseAddr(expr)
	if err != nil {
		return netip.Prefix{}, fmt.Errorf("invalid IP address: '%s': %v", expr, err)
	}
	prefix := netip.PrefixFrom(parsed, parsed.BitLen())
	return prefix, nil
}

// Interface guards
var (
	_ caddy.Provisioner     = (*StaticIPRange)(nil)
	_ caddyfile.Unmarshaler = (*StaticIPRange)(nil)
	_ IPRangeSource         = (*StaticIPRange)(nil)
)

// PrivateRangesCIDR returns a list of private CIDR range
// strings, which can be used as a configuration shortcut.
// Note: this function is used at least by mholt/caddy-l4.
func PrivateRangesCIDR() []string {
	return internal.PrivateRangesCIDR()
}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use literal IPs only: ip_range 10.0.0.1.
  2. Strip empty lines/whitespace from generated lists.
  3. Avoid leading-zero octet notation; write 10.0.0.1, not 010.0.0.1.
  4. Validate each token with netip.ParseAddr in your config-generation pipeline.

Example fix

// before
ip_range 010.0.0.1

// after
ip_range 10.0.0.1
Defensive patterns

Strategy: validation

Validate before calling

import "net/netip"

func validSingleIPs(entries []string) bool {
	for _, e := range entries {
		if !strings.Contains(e, "/") {
			if _, err := netip.ParseAddr(e); err != nil {
				return false
			}
		}
	}
	return true
}

Prevention

When it happens

Trigger: A range entry that is a hostname (`ip_range 10.0.0.1 example.com`), a malformed address (`10.0.0`), or an empty token. Also any string Go's netip rejects, such as addresses with leading zeros (`010.0.0.1`).

Common situations: DNS names pasted into IP range lists; CSV/CMDB exports with blank or commented lines not filtered; environments where leading-zero octets were accepted by older parsers (inet_pton-style tools) but netip forbids them.

Related errors


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