{"record":{"id":"449b5e046cef6a3f","repo":"caddyserver/caddy","slug":"parsing-cidr-expression-s-v","errorCode":null,"errorMessage":"parsing CIDR expression '%s': %v","messagePattern":"parsing CIDR expression '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/ip_matchers.go","lineNumber":302,"sourceCode":"func provisionCidrsZonesFromRanges(ranges []string) ([]*netip.Prefix, []string, error) {\n\tcidrs := []*netip.Prefix{}\n\tzones := []string{}\n\trepl := caddy.NewReplacer()\n\tfor _, str := range ranges {\n\t\tstr = repl.ReplaceAll(str, \"\")\n\t\t// Exclude the zone_id from the IP\n\t\tif strings.Contains(str, \"%\") {\n\t\t\tsplit := strings.Split(str, \"%\")\n\t\t\tstr = split[0]\n\t\t\t// write zone identifiers in m.zones for matching later\n\t\t\tzones = append(zones, split[1])\n\t\t} else {\n\t\t\tzones = append(zones, \"\")\n\t\t}\n\t\tif strings.Contains(str, \"/\") {\n\t\t\tipNet, err := netip.ParsePrefix(str)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"parsing CIDR expression '%s': %v\", str, err)\n\t\t\t}\n\t\t\tcidrs = append(cidrs, &ipNet)\n\t\t} else {\n\t\t\tipAddr, err := netip.ParseAddr(str)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"invalid IP address: '%s': %v\", str, err)\n\t\t\t}\n\t\t\tipNew := netip.PrefixFrom(ipAddr, ipAddr.BitLen())\n\t\t\tcidrs = append(cidrs, &ipNew)\n\t\t}\n\t}\n\treturn cidrs, zones, nil\n}\n\nfunc parseIPZoneFromString(address string) (netip.Addr, string, error) {\n\tipStr, _, err := net.SplitHostPort(address)\n\tif err != nil {\n\t\tipStr = address // OK; probably didn't have a port","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/ip_matchers.go#L284-L320","documentation":"Thrown while provisioning the remote_ip/client_ip matcher when an entry contains '/' and is therefore treated as a CIDR, but netip.ParsePrefix rejects it. The string is passed through verbatim in the message, so the offending entry is visible. This is a config-load-time error: Caddy refuses to start/apply the config containing the bad matcher.","triggerScenarios":"Configuring `remote_ip 192.168.1.0/33` (mask too long), `10.0.0.0/8/24`, an IPv6 prefix like `fe80::/10x`, or a bare IP with a trailing slash `10.0.0.1/`. Also triggered when a zone identifier was stripped (everything after '%') leaving a malformed prefix.","commonSituations":"Typos in subnet masks in a Caddyfile or JSON config; assuming an arbitrary bit count is valid (IPv4 max /32, IPv6 max /128); copy-pasting routes that include both an IP and mask separated incorrectly; IPv4-mapped IPv6 notation that netip refuses in prefixes.","solutions":["Correct the CIDR to a valid prefix: IPv4 masks 0-32, IPv6 masks 0-128 (e.g. 192.168.1.0/24).","If you meant a single host, drop the slash: remote_ip 10.0.0.1.","Verify entries before deploying with a quick Go check: netip.ParsePrefix(\"192.168.1.0/24\").","Run `caddy adapt --config Caddyfile --adapter caddyfile` then `caddy validate` to catch this before reload."],"exampleFix":"// before (Caddyfile)\n@internal remote_ip 192.168.1.0/33\n\n// after\n@internal remote_ip 192.168.1.0/24","handlingStrategy":"validation","validationCode":"// Go: pre-validate every CIDR entry before building the config\nimport \"net/netip\"\n\nfunc validCIDRs(entries []string) bool {\n\tfor _, e := range entries {\n\t\tif !strings.Contains(e, \"/\") {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := netip.ParsePrefix(e); err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `caddy validate --config <file>` in CI before deploy.","Generate IP lists from typed sources (netip.Prefix values), not free-form strings.","Remember bounds: IPv4 /0-/32, IPv6 /0-/128."],"tags":["caddy","config","network","cidr","ipv4","ipv6"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}