{"record":{"id":"54fb9a9c061eb72c","repo":"grafana/k6","slug":"negative-ip-range-s","errorCode":null,"errorMessage":"negative IP range: {s}","messagePattern":"negative IP range: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/types/ipblock.go","lineNumber":55,"sourceCode":"\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)\n\tblock.ipv6 = ip0.To4() == nil\n\tif block.ipv6 {\n\t\tblock.firstIP.SetBytes(ip0.To16())\n\t\tblock.count.SetBytes(ip1.To16())\n\t} else {\n\t\tblock.firstIP.SetBytes(ip0.To4())\n\t\tblock.count.SetBytes(ip1.To4())\n\t}","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/lib/types/ipblock.go#L37-L73","documentation":"Returned by ipBlockFromRange (lib/types/ipblock.go:54-56) when the range's start IP is strictly greater than its end IP, i.e. ipBlockFromTwoIPs computes a non-positive count (big.Int count.Sign() <= 0). Ranges are ordered: the block's size is end-start (+1), so a reversed range yields zero or negative capacity and is rejected instead of silently producing an empty block.","triggerScenarios":"Calling getIPBlock with '192.168.0.100-192.168.0.1' or '::ff-::1' — both endpoints valid, same family, but descending order.","commonSituations":"Sorting bugs in generated configs (start/end fields swapped); UIs or scripts that let users enter 'from'/'to' IPs without ordering validation.","solutions":["Swap the endpoints so the smaller IP comes first: '192.168.0.1-192.168.0.100'","If generating programmatically, compare with ip0.Compare(ip1) (Go 1.22+) before formatting the range"],"exampleFix":"// before\nblock, err := getIPBlock(\"192.168.0.100-192.168.0.1\")\n\n// after\nblock, err := getIPBlock(\"192.168.0.1-192.168.0.100\")","handlingStrategy":"validation","validationCode":"// Go: order endpoints before building the range string\nfunc orderedRange(a, b net.IP) string {\n    if a.Compare(b) > 0 {\n        a, b = b, a\n    }\n    return a.String() + \"-\" + b.String()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat 'from' as min and 'to' as max in any UI or config that produces ranges","Net IP Compare (Go 1.22+) gives a cheap pre-check: reject or swap when from > to"],"tags":["config","ipblock","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}