{"record":{"id":"e1ef46f9acba68ee","repo":"XTLS/Xray-core","slug":"failed-to-build-ipv4-set","errorCode":null,"errorMessage":"failed to build IPv4 set","messagePattern":"failed to build IPv4 set","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/geodata/ip_matcher.go","lineNumber":914,"sourceCode":"\t\t}\n\n\t\tif addr.Is4() {\n\t\t\tipv4Builder.AddPrefix(prefix)\n\t\t} else if addr.Is6() {\n\t\t\tipv6Builder.AddPrefix(prefix)\n\t\t}\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// peak mem\n\truntime.GC()\n\tdefer runtime.GC()\n\n\tipv4, err := ipv4Builder.IPSet()\n\tif err != nil {\n\t\treturn nil, errors.New(\"failed to build IPv4 set\").Base(err)\n\t}\n\tipv6, err := ipv6Builder.IPSet()\n\tif err != nil {\n\t\treturn nil, errors.New(\"failed to build IPv6 set\").Base(err)\n\t}\n\n\tvar has4, has6 bool\n\tvar max4, max6 int\n\n\tfor _, p := range ipv4.Prefixes() {\n\t\thas4 = true\n\t\tif b := p.Bits(); b > max4 {\n\t\t\tmax4 = b\n\t\t}\n\t}\n\tfor _, p := range ipv6.Prefixes() {\n\t\thas6 = true\n\t\tif b := p.Bits(); b > max6 {","sourceCodeStart":896,"sourceCodeEnd":932,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/common/geodata/ip_matcher.go#L896-L932","documentation":"Wrapped error from IPSetFactory.createFrom (common/geodata/ip_matcher.go:914) when netipx.IPSetBuilder.IPSet() fails to materialize the accumulated IPv4 prefixes. Note that obviously invalid CIDRs (bad IP slices, invalid prefix lengths) are skipped with a log line before this point, so this error means the builder itself rejected the (apparently valid) prefix set — e.g. inconsistent prefix families or malformed add state. The base error carries go4.org/netipx's reason.","triggerScenarios":"CreateFromCIDRs / createFrom over GeoIP-derived CIDRs where a prefix passes the AddrFromSlice/PrefixFrom validity checks but still breaks IPSetBuilder — mismatched 4/6 handling of mapped addresses (v4-in-v6 byte slices), or data that yields a builder state netipx refuses to combine.","commonSituations":"Custom geoip.dat files with IPv4 addresses stored as 16-byte v4-in-v6 representations; dat files from converters that emit unusual prefix encodings; core/netipx version changes altering acceptance rules.","solutions":["Inspect the Base error for the netipx reason; log the CIDRs fed in (add temporary logging around the yield callback) to find the offending entry.","Regenerate/replace the geoip.dat with the official build; custom converters must emit canonical 4-byte IPv4 / 16-byte IPv6 with sane prefix lengths.","Update xray-core (and its vendored netipx) — acceptance of mapped addresses has evolved between versions."],"exampleFix":"// before (custom dat generator)\ncidr.Ip = net.ParseIP(\"1.2.3.4\").To16() // v4-in-v6 slice -> ambiguous family\n\n// after\nip := net.ParseIP(\"1.2.3.4\")\ncidr.Ip = ip.To4() // canonical 4-byte IPv4 for IPv4 CIDRs","handlingStrategy":"try-catch","validationCode":"// Pre-filter CIDRs for family consistency before CreateFromCIDRs:\nfor _, c := range cidrs {\n    if a, ok := netip.AddrFromSlice(c.GetIp()); !ok || (a.Is4() != len(c.GetIp()) == 4) { /* fix or drop */ }\n}","typeGuard":"func isCanonicalCIDR(c *geodata.CIDR) bool {\n    a, ok := netip.AddrFromSlice(c.GetIp())\n    return ok && netip.PrefixFrom(a, int(c.GetPrefix())).IsValid() && !(a.Is4() && len(c.GetIp()) == 16)\n}","tryCatchPattern":"set, err := factory.CreateFromCIDRs(cidrs)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to build IPv4 set\") {\n        // inspect err base for netipx reason; drop offending prefixes and rebuild\n    }\n    return err\n}","preventionTips":["Emit canonical 4-byte IPv4 / 16-byte IPv6 in custom dat generators","Use official geoip.dat builds","Keep core and vendored netipx current"],"tags":["geodata","geoip","netip","routing","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}