MHSanaei/3x-ui · error

host %s has no IP addresses

Error message

host %s has no IP addresses

What it means

rejectPrivateHost reports this when DNS resolution succeeds with an error but returns an empty IP list — a rare resolver state distinct from NXDOMAIN (which errors). It means the name exists in some sense but the resolver handed back zero A/AAAA records, so the panel cannot even evaluate the SSRF blocklist for it.

Source

Thrown at internal/web/service/url_safety.go:76

	if err := rejectPrivateHost(ctx, u.Hostname()); err != nil {
		return "", err
	}
	return clean, nil
}

func rejectPrivateHost(ctx context.Context, hostname string) error {
	if ip := net.ParseIP(hostname); ip != nil {
		if isBlockedIP(ip) {
			return fmt.Errorf("blocked private/internal address %s", ip.String())
		}
		return nil
	}
	ips, err := net.DefaultResolver.LookupIPAddr(ctx, hostname)
	if err != nil {
		return fmt.Errorf("cannot resolve host %s: %w", hostname, err)
	}
	if len(ips) == 0 {
		return fmt.Errorf("host %s has no IP addresses", hostname)
	}
	for _, ipAddr := range ips {
		if isBlockedIP(ipAddr.IP) {
			return fmt.Errorf("host %s resolves to blocked private/internal address %s", hostname, ipAddr.IP.String())
		}
	}
	return nil
}

func isBlockedIP(ip net.IP) bool {
	return netsafe.IsBlockedIP(ip)
}

View on GitHub (pinned to ad32144c42)

Solutions

  1. Check what records the name actually has: 'dig <host> A <host> AAAA +short' — if empty, the name is wrong for this purpose; use the name that owns address records.
  2. If split-horizon DNS is involved, ensure the panel's resolver serves the view with A/AAAA records.
  3. Treat as a configuration error, not transient: an empty answer rarely self-heals; fix the name.

Example fix

// before
url := "https://mailonly.example.com/health" // name has only MX/TXT records

// after
url := "https://www.example.com/health" // name with A/AAAA records
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the configured name actually has address records before relying on it
addrs, err := net.LookupHost(hostname)
if err != nil || len(addrs) == 0 {
    return fmt.Errorf("hostname %q has no A/AAAA records", hostname)
}

Type guard

null

Try / catch

if strings.Contains(err.Error(), "no IP addresses") {
    // permanent config error: pick a name that owns A/AAAA records
}

Prevention

When it happens

Trigger: Hostnames with only non-address records (e.g. a name that has only an MX/TXT record and no A/AAAA), or aresolver/misconfigured DNS middleware returning empty NOERROR answers.

Common situations: Pointing a health-check at a mail-only or TXT-only name; DNS firewalls (Response Policy Zones) that strip answers; some split-horizon setups where the public view has no A record.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/c31da463a4b8e59f. Report an issue: GitHub.