{"record":{"id":"ab4c788e7104c882","repo":"XTLS/Xray-core","slug":"failed-to-build-ipv6-set","errorCode":null,"errorMessage":"failed to build IPv6 set","messagePattern":"failed to build IPv6 set","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/geodata/ip_matcher.go","lineNumber":918,"sourceCode":"\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 {\n\t\t\tmax6 = b\n\t\t}\n\t}\n","sourceCodeStart":900,"sourceCodeEnd":936,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/common/geodata/ip_matcher.go#L900-L936","documentation":"Thrown when the go4org/netiproute IP-set builder fails to materialize the accumulated IPv6 CIDR list into an immutable IPSet during geodata IP matcher construction. The IPv4 build has already succeeded at this point, so the failure is specific to IPv6 prefix data. It is always wrapped around a Base(err) from the underlying builder (e.g. malformed IPv6 prefix, out-of-memory while inserting many prefixes).","triggerScenarios":"Calling the geodata IP matcher builder (e.g. via GetOrCreateFromGeoIPRules / newHeuristicIPMatcher) where the GeoIP dat file or custom CIDR list contains an IPv6 entry the ipset builder rejects; extremely large rule sets exhausting memory during IPSet() finalization.","commonSituations":"Corrupted or non-standard geosite/geolocation dat files (e.g. a Country.mmdb mistakenly used as .dat); hand-written route rules with IPv6 CIDRs of wrong byte length; upgrading Xray with an old dat file format; memory-constrained containers loading the full IPv6 delegation file.","solutions":["Inspect the wrapped base error to see which IPv6 prefix failed; remove or fix that entry in the rule list.","Regenerate/re-download geoip.dat (and any ext: files) from the official LXGI/runetfreedom style sources to rule out corruption.","If loading 'geoip:cn'-style large sets in a low-memory environment, raise container memory or split rules.","Verify each custom IPv6 CIDR with netip.ParsePrefix before adding it to config."],"exampleFix":"// before\n\"ip\": [\"2001:db8::/33-bad\"] // typo'd / impossible prefix\n\n// after\n\"ip\": [\"2001:db8::/33\"]","handlingStrategy":"validation","validationCode":"for _, c := range ipRules {\n    if strings.Contains(c, \":\") && !strings.HasPrefix(c, \"geoip:\") && !strings.HasPrefix(c, \"ext:\") {\n        continue\n    }\n    for _, part := range strings.FieldsFunc(c, func(r rune) bool { return r == '/' }) {\n        if strings.Contains(part, \":\") {\n            if _, err := netip.ParsePrefix(part); err != nil { return fmt.Errorf(\"bad IPv6 rule %q: %w\", c, err) }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if ipset, err := matcherHolder.GetIPMatcher(); err != nil {\n    log.Warn(\"geodata ipset build failed, falling back to linear matcher: \", err)\n    ipset = buildLinearFallback() // or abort config load\n}","preventionTips":["Validate all custom IPv6 CIDRs with netip.ParsePrefix before config load.","Keep geoip dat files from a single trusted source and regenerate on upgrade.","Smoke-test full rule sets in a low-memory staging replica before deploy."],"tags":["geodata","ipv6","ipset","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}