{"record":{"id":"7b7e91b402fafa38","repo":"MHSanaei/3x-ui","slug":"no-usable-address-for-s","errorCode":null,"errorMessage":"no usable address for %s","messagePattern":"no usable address for (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/util/netsafe/netsafe.go","lineNumber":58,"sourceCode":"\t\tips, err = net.DefaultResolver.LookupIPAddr(ctx, host)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvar lastErr error\n\tfor _, ipAddr := range ips {\n\t\tif !allowPrivate && IsBlockedIP(ipAddr.IP) {\n\t\t\tlastErr = fmt.Errorf(\"blocked private/internal address %s\", ipAddr.IP)\n\t\t\tcontinue\n\t\t}\n\t\tconn, derr := defaultDialer.DialContext(ctx, network, net.JoinHostPort(ipAddr.IP.String(), port))\n\t\tif derr == nil {\n\t\t\treturn conn, nil\n\t\t}\n\t\tlastErr = derr\n\t}\n\tif lastErr == nil {\n\t\tlastErr = fmt.Errorf(\"no usable address for %s\", host)\n\t}\n\treturn nil, lastErr\n}\n\nvar hostnamePattern = regexp.MustCompile(`^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$`)\n\nfunc NormalizeHost(addr string) (string, error) {\n\taddr = strings.TrimSpace(addr)\n\tif addr == \"\" {\n\t\treturn \"\", fmt.Errorf(\"address is required\")\n\t}\n\tif strings.HasPrefix(addr, \"[\") && strings.HasSuffix(addr, \"]\") {\n\t\taddr = addr[1 : len(addr)-1]\n\t}\n\tif ip := net.ParseIP(addr); ip != nil {\n\t\treturn ip.String(), nil\n\t}\n\tif len(addr) > 253 || !hostnamePattern.MatchString(addr) {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/util/netsafe/netsafe.go#L40-L76","documentation":"In netsafe's guarded dial loop, 'no usable address for %s' is the fallback when lastErr is still nil after iterating every resolved IP — which can only happen when the IP list itself was empty (a DNS answer with zero records). If any dial failed, that dial error is returned instead; if any IP was blocked, the block error is returned instead. So this specific message means: host resolved successfully but yielded no addresses at all.","triggerScenarios":"A hostname with a DNS response containing zero A/AAAA records (e.g. a name that exists only as TXT/CNAME-without-target, or NODATA), passed to the safe dialer.","commonSituations":"Typo'd subscription host that still has a DNS zone; a domain whose records were removed but zone remains; a stale node address; DNS middleware (some resolvers) returning success-with-no-answers for filtered domains.","solutions":["Verify the host actually has A/AAAA records: `dig +short HOST A` and `dig +short HOST AAAA`.","If the domain's records were removed, restore them or update the configured address.","If a CNAME, ensure the target resolves.","Switch to a resolver without filtering if your DNS provider returns empty answers for blocked domains."],"exampleFix":"// before\nconn, err := safeDial(ctx, \"tcp\", \"stale.example.com:443\") // no usable address\n\n// after: fix DNS first\n// $ dig +short stale.example.com A  -> empty, repoint or fix records\nconn, err := safeDial(ctx, \"tcp\", \"live.example.com:443\")","handlingStrategy":"validation","validationCode":"func resolvesToAddresses(host string) bool {\n\tips, err := net.LookupIP(host)\n\treturn err == nil && len(ips) > 0\n}","typeGuard":null,"tryCatchPattern":"conn, err := safeDial(ctx, network, addr)\nif err != nil && strings.Contains(err.Error(), \"no usable address\") {\n    return fmt.Errorf(\"host %s resolves to no A/AAAA records; check DNS\", hostOnly(addr))\n}","preventionTips":["Pre-check DNS with LookupIP before dialing user-supplied hosts.","Surface DNS diagnostics in error UIs instead of retrying blindly.","Alert when a configured hostname's record count drops to zero."],"tags":["network","dns","dialer","configuration"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}