{"record":{"id":"7188052c6ba624e0","repo":"caddyserver/caddy","slug":"parsing-cidr-expression-s-v-718805","errorCode":null,"errorMessage":"parsing CIDR expression: '%s': %v","messagePattern":"parsing CIDR expression: '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/ip_range.go","lineNumber":111,"sourceCode":"\t}\n\tfor d.NextArg() {\n\t\tif d.Val() == \"private_ranges\" {\n\t\t\tm.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...)\n\t\t\tcontinue\n\t\t}\n\t\tm.Ranges = append(m.Ranges, d.Val())\n\t}\n\treturn nil\n}\n\n// CIDRExpressionToPrefix takes a string which could be either a\n// CIDR expression or a single IP address, and returns a netip.Prefix.\nfunc CIDRExpressionToPrefix(expr string) (netip.Prefix, error) {\n\t// Having a slash means it should be a CIDR expression\n\tif strings.Contains(expr, \"/\") {\n\t\tprefix, err := netip.ParsePrefix(expr)\n\t\tif err != nil {\n\t\t\treturn netip.Prefix{}, fmt.Errorf(\"parsing CIDR expression: '%s': %v\", expr, err)\n\t\t}\n\t\treturn prefix, nil\n\t}\n\n\t// Otherwise it's likely a single IP address\n\tparsed, err := netip.ParseAddr(expr)\n\tif err != nil {\n\t\treturn netip.Prefix{}, fmt.Errorf(\"invalid IP address: '%s': %v\", expr, err)\n\t}\n\tprefix := netip.PrefixFrom(parsed, parsed.BitLen())\n\treturn prefix, nil\n}\n\n// Interface guards\nvar (\n\t_ caddy.Provisioner     = (*StaticIPRange)(nil)\n\t_ caddyfile.Unmarshaler = (*StaticIPRange)(nil)\n\t_ IPRangeSource         = (*StaticIPRange)(nil)","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/ip_range.go#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the entry to a valid prefix (IPv4 /0-/32, IPv6 /0-/128), e.g. 172.16.0.0/12.","For a single address omit the slash entirely.","Lint generated lists with netip.ParsePrefix before pushing config.","Use `caddy validate --config <file>` in CI to catch provisioning errors pre-deploy."],"exampleFix":"// before (Caddyfile)\nip_range 172.16.0.0/12/12\n\n// after\nip_range 172.16.0.0/12","handlingStrategy":"validation","validationCode":"import \"net/netip\"\n\nfunc validRangeEntries(entries []string) bool {\n\tfor _, e := range entries {\n\t\tif strings.Contains(e, \"/\") {\n\t\t\tif _, err := netip.ParsePrefix(e); err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint ip_range lists with netip.ParsePrefix in config pipelines.","Keep one entry per token; no combined ranges like 10.0.0.0-10.0.0.9.","Use caddy validate as a pre-deploy gate."],"tags":["caddy","config","cidr","ip-range","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}