caddyserver/caddy · error

parsing CIDR expression: '%s': %v

Error message

parsing CIDR expression: '%s': %v

What it means

CIDRExpressionToPrefix (modules/caddyhttp/ip_range.go) converts one entry of an IP range source (e.g. the static `source_ip_range` / `internal_ip_range`-style modules) into a netip.Prefix. When the entry contains '/', netip.ParsePrefix is used; failure produces this wrapped error during Provision. The offending expression is included verbatim.

Source

Thrown at modules/caddyhttp/ip_range.go:111

	}
	for d.NextArg() {
		if d.Val() == "private_ranges" {
			m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...)
			continue
		}
		m.Ranges = append(m.Ranges, d.Val())
	}
	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)

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Fix the entry to a valid prefix (IPv4 /0-/32, IPv6 /0-/128), e.g. 172.16.0.0/12.
  2. For a single address omit the slash entirely.
  3. Lint generated lists with netip.ParsePrefix before pushing config.
  4. Use `caddy validate --config <file>` in CI to catch provisioning errors pre-deploy.

Example fix

// before (Caddyfile)
ip_range 172.16.0.0/12/12

// after
ip_range 172.16.0.0/12
Defensive patterns

Strategy: validation

Validate before calling

import "net/netip"

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

Prevention

When it happens

Trigger: A range entry like `172.16.0.0/12/12`, mask out of family range (`2001:db8::/129`), or a value like `10.0.0.5/` after Caddyfile tokenizing. Anything with a slash but not a syntactically valid prefix.

Common situations: Caddyfile `ip_range` / range source blocks with typo'd masks; mixing up the argument order in directives; generating range lists from CMDB exports that contain stray '/' characters; version migrations where old configs tolerated looser parsing.

Related errors


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