{"record":{"id":"a9a54c02ff64be63","repo":"caddyserver/caddy","slug":"invalid-ip-address-s-v-a9a54c","errorCode":null,"errorMessage":"invalid IP address: '%s': %v","messagePattern":"invalid IP address: '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/ip_range.go","lineNumber":119,"sourceCode":"\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)\n)\n\n// PrivateRangesCIDR returns a list of private CIDR range\n// strings, which can be used as a configuration shortcut.\n// Note: this function is used at least by mholt/caddy-l4.\nfunc PrivateRangesCIDR() []string {\n\treturn internal.PrivateRangesCIDR()\n}","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/ip_range.go#L101-L137","documentation":"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.","triggerScenarios":"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`).","commonSituations":"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.","solutions":["Use literal IPs only: ip_range 10.0.0.1.","Strip empty lines/whitespace from generated lists.","Avoid leading-zero octet notation; write 10.0.0.1, not 010.0.0.1.","Validate each token with netip.ParseAddr in your config-generation pipeline."],"exampleFix":"// before\nip_range 010.0.0.1\n\n// after\nip_range 10.0.0.1","handlingStrategy":"validation","validationCode":"import \"net/netip\"\n\nfunc validSingleIPs(entries []string) bool {\n\tfor _, e := range entries {\n\t\tif !strings.Contains(e, \"/\") {\n\t\t\tif _, err := netip.ParseAddr(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":["Filter hostnames and empty strings out of generated IP lists.","Write 10.0.0.1, never 010.0.0.1.","Prefer subnets (/32, /128) over long single-IP lists."],"tags":["caddy","config","ip-address","ip-range","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}