jeessy2/ddns-go · error

interface %s has no usable %s address

Error message

interface %s has no usable %s address

What it means

This error is thrown by getLocalAddrFromInterfaceByNetwork when the named network interface has at least one global-unicast address, but none of them match the requested address family (tcp4 or tcp6). The library binds the HTTP client's dialer to a specific interface and must pick an IP of the family matching the forced dial network; if the interface only has addresses of the other family (e.g. only IPv6 link-local/global addresses while 'tcp4' was requested), it cannot return a usable bind address and fails. Callers (CreateBoundNoProxyHTTPClient) log the failure and fall back to the default interface.

Source

Thrown at util/http_client_util.go:34

	}
	addrs, err := iface.Addrs()
	if err != nil {
		return "", fmt.Errorf("get interface %s addresses failed: %v", ifaceName, err)
	}

	hasGlobalUnicast := false
	for _, addr := range addrs {
		ipNet, ok := addr.(*net.IPNet)
		if !ok || !ipNet.IP.IsGlobalUnicast() {
			continue
		}
		hasGlobalUnicast = true
		if isIPMatchedNetwork(ipNet.IP, network) {
			return ipNet.IP.String(), nil
		}
	}
	if hasGlobalUnicast && (network == "tcp4" || network == "tcp6") {
		return "", fmt.Errorf("interface %s has no usable %s address", ifaceName, network)
	}
	return "", fmt.Errorf("interface %s has no usable global-unicast address", ifaceName)
}

func isIPMatchedNetwork(ip net.IP, network string) bool {
	switch network {
	case "tcp4":
		return ip.To4() != nil
	case "tcp6":
		return ip.To16() != nil && ip.To4() == nil
	default:
		return true
	}
}

func localTCPAddr(ip net.IP, network, ifaceName string) *net.TCPAddr {
	addr := &net.TCPAddr{IP: ip}
	if network == "tcp6" && ifaceName != "" {

View on GitHub (pinned to 5874c2e666)

Solutions

  1. Check the interface's actual addresses (ip addr show <iface>) and pass the network that matches the addresses present: "tcp4" for an IPv4-only NIC, "tcp6" for an IPv6-only NIC
  2. Use network "tcp" (or empty/default) instead of "tcp4"/"tcp6" so any global-unicast address on the interface is accepted
  3. Add the missing address family to the interface (e.g. enable IPv6 or assign an IPv4 address) if the forced family is required
  4. Pick a different interface that has an address of the requested family

Example fix

// before
client := util.CreateBoundNoProxyHTTPClient("tcp4", "eth0") // eth0 is IPv6-only
// after
client := util.CreateBoundNoProxyHTTPClient("tcp", "eth0") // accept any family on eth0
Defensive patterns

Strategy: validation

Validate before calling

func hasFamilyAddr(ifaceName, network string) bool {
	iface, err := net.InterfaceByName(ifaceName)
	if err != nil { return false }
	addrs, _ := iface.Addrs()
	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
			if network == "tcp4" && ipnet.IP.To4() != nil { return true }
			if network == "tcp6" && ipnet.IP.To4() == nil { return true }
			if network != "tcp4" && network != "tcp6" { return true }
		}
	}
	return false
}
// call util.CreateBoundNoProxyHTTPClient only if hasFamilyAddr(iface, network)

Try / catch

localIP, err := util.GetLocalAddrFromInterface(ifaceName)
if err != nil {
	log.Printf("interface %s lacks %s address, using default: %v", ifaceName, network, err)
	client = util.CreateNoProxyHTTPClient(network) // fallback
}

Prevention

When it happens

Trigger: Calling CreateBoundNoProxyHTTPClient("tcp4", ifaceName) on an interface whose addresses are all IPv6, or CreateBoundNoProxyHTTPClient("tcp6", ifaceName) on an interface with only IPv4 addresses. The interface must exist and have a global-unicast address, but not of the requested family.

Common situations: Configuring an IPv4-only box/interface for an IPv6-forced client or vice versa; dual-stack servers reached via a tunnel interface that only has one family; containers/veth pairs provisioned with only one address family; typos forcing 'tcp6' in config when the chosen NIC is IPv4-only.

Related errors


AI-assisted analysis of jeessy2/ddns-go@5874c2e666 (2026-09-03). Data as JSON: /api/errors/093b95ace61f35d6. Report an issue: GitHub.