{"record":{"id":"f63ccde9404ae755","repo":"grafana/k6","slug":"wrong-ip-range-format-s","errorCode":null,"errorMessage":"wrong IP range format: {s}","messagePattern":"wrong IP range format: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/types/ipblock.go","lineNumber":47,"sourceCode":"func getIPBlock(s string) (*ipBlock, error) {\n\tswitch {\n\tcase strings.Contains(s, \"-\"):\n\t\treturn ipBlockFromRange(s)\n\tcase strings.Contains(s, \"/\"):\n\t\treturn ipBlockFromCIDR(s)\n\tdefault:\n\t\tif net.ParseIP(s) == nil {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid IP, IP range or CIDR\", s)\n\t\t}\n\t\treturn ipBlockFromRange(s + \"-\" + s)\n\t}\n}\n\nfunc ipBlockFromRange(s string) (*ipBlock, error) {\n\tip0Str, ip1Str, _ := strings.Cut(s, \"-\")\n\tip0, ip1 := net.ParseIP(ip0Str), net.ParseIP(ip1Str)\n\tif ip0 == nil || ip1 == nil {\n\t\treturn nil, errors.New(\"wrong IP range format: \" + s)\n\t}\n\tif (ip0.To4() == nil) != (ip1.To4() == nil) { // XOR\n\t\treturn nil, errors.New(\"mixed IP range format: \" + s)\n\t}\n\tblock := ipBlockFromTwoIPs(ip0, ip1)\n\n\tif block.count.Sign() <= 0 {\n\t\treturn nil, errors.New(\"negative IP range: \" + s)\n\t}\n\treturn block, nil\n}\n\nfunc ipBlockFromTwoIPs(ip0, ip1 net.IP) *ipBlock {\n\t// This code doesn't do any checks on the validity of the arguments, that should be\n\t// done before and/or after it is called\n\tvar block ipBlock\n\tblock.firstIP = new(big.Int)\n\tblock.count = new(big.Int)","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/lib/types/ipblock.go#L29-L65","documentation":"Returned by ipBlockFromRange (lib/types/ipblock.go:46-48) when parsing an IP range 'start-end' where at least one side fails net.ParseIP. getIPBlock splits the string on the first '-' (strings.Cut, line 42) and both halves must be valid literal IPv4 or IPv6 addresses; this constructor is reached from the range branch (and from the single-IP default branch which synthesizes 'ip-ip').","triggerScenarios":"Calling getIPBlock (via the types.IPBlock parsing surface, ipblock.go:124) with values like '192.168.0.256-192.168.0.300' (out-of-range octets), '192.168.0-192.168.0.5' (incomplete address), 'foo-bar', or a CIDR string that fell through to the range branch.","commonSituations":"Typos in IP allowlists/blocklists in configs; specifying a hostnames or domain where an IP range is required (names are never resolved here); truncated values from templating.","solutions":["Provide both endpoints as full literal IPs: '192.168.0.1-192.168.0.100'","For a single IP, pass just '192.168.0.1' (the default branch wraps it into a one-IP range)","For a network, prefer CIDR notation like '192.168.0.0/24', which is handled before the range branch"],"exampleFix":"// before\nblock, err := getIPBlock(\"192.168.0.300-192.168.0.400\")\n\n// after\nblock, err := getIPBlock(\"192.168.0.1-192.168.0.100\")","handlingStrategy":"validation","validationCode":"// Go: validate an IP range string before using it as a types IP block\nfunc validateIPRange(s string) error {\n    a, b, ok := strings.Cut(s, \"-\")\n    if !ok {\n        return fmt.Errorf(\"%q is not a range; expected 'startIP-endIP'\", s)\n    }\n    if net.ParseIP(a) == nil || net.ParseIP(b) == nil {\n        return fmt.Errorf(\"%q: both endpoints must be literal IPs\", s)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass literal IPs, not hostnames — this parser never resolves DNS","Prefer CIDR notation ('192.168.0.0/24') for subnets; it is handled by a dedicated branch"],"tags":["config","ipblock","network","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}