txthinking/brook · error

no ipv6 from nic

Error message

no ipv6 from nic

What it means

The DialWithNIC plugin's IP method enumerates the local interface's addresses (addrs) to pick a source IP for dialing. When v46 == "6", it scans the list for a globally-unicast, non-IPv4 (IPv6) address; this error means no address in the list matched, so no usable global IPv6 source address exists on the selected NIC. The library throws it instead of dialing with a link-local or absent IPv6 address.

Source

Thrown at plugins/dialwithnic/dialwithnic.go:49

	return d
}

func (p *DialWithNIC) IP(v46 string) (net.IP, error) {
	ief, err := net.InterfaceByName(p.NIC)
	if err != nil {
		return nil, err
	}
	addrs, err := ief.Addrs()
	if err != nil {
		return nil, err
	}
	if v46 == "6" {
		for _, v := range addrs {
			if v.(*net.IPNet).IP.IsGlobalUnicast() && v.(*net.IPNet).IP.To4() == nil {
				return v.(*net.IPNet).IP, nil
			}
		}
		return nil, errors.New("no ipv6 from nic")
	}
	if v46 == "4" {
		for _, v := range addrs {
			if v.(*net.IPNet).IP.IsGlobalUnicast() && !v.(*net.IPNet).IP.IsPrivate() && v.(*net.IPNet).IP.To4() != nil {
				return v.(*net.IPNet).IP, nil
			}
		}
		for _, v := range addrs {
			if v.(*net.IPNet).IP.IsGlobalUnicast() && v.(*net.IPNet).IP.IsPrivate() && v.(*net.IPNet).IP.To4() != nil {
				return v.(*net.IPNet).IP, nil
			}
		}
		return nil, errors.New("no ipv4 from nic")
	}
	return nil, errors.New("black hole")
}

func (p *DialWithNIC) TouchBrook() {

View on GitHub (pinned to 5cd13ef3b1)

Solutions

  1. Enable global IPv6 on the host/interface (check `ip -6 addr`, router advertisements, SLAAC/DHCPv6) so a global unicast address exists.
  2. If IPv6 is unavailable, configure the plugin for IPv4 instead (pass v46="4") or use the plain dialer without a source-NIC constraint.
  3. Inspect interface addresses with net.InterfaceAddrs() to confirm which families are present before selecting v46.
  4. Fall back gracefully in the caller: treat this error as 'no IPv6 connectivity' and retry over IPv4.

Example fix

// before
ip, err := plugin.IP("6") // fails on IPv4-only host
// after
ip, err := plugin.IP("6")
if err != nil {
    ip, err = plugin.IP("4") // fall back to IPv4
}
Defensive patterns

Strategy: fallback

Validate before calling

func hasGlobalIPv6() bool {
    for _, a := range interfaceAddrs() {
        if ipnet, ok := a.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() && ipnet.IP.To4() == nil {
            return true
        }
    }
    return false
}
// only call plugin.IP("6") if hasGlobalIPv6()

Type guard

func isGlobalIPv6(ip net.IP) bool {
    return ip.IsGlobalUnicast() && ip.To4() == nil
}

Try / catch

ip, err := plugin.IP("6")
if err != nil && err.Error() == "no ipv6 from nic" {
    log.Println("no global IPv6 on NIC, falling back to IPv4")
    ip, err = plugin.IP("4")
}

Prevention

When it happens

Trigger: Calling IP(v46="6") (directly or via DialWithNIC's TouchBrook wiring) when the interface's addr list contains no global unicast IPv6 address — e.g. only IPv4 or only link-local fe80:: addresses — typically because the host/NIC has no global IPv6 connectivity.

Common situations: Running on an IPv4-only host, VM, or container; IPv6 disabled via kernel sysctls (disable_ipv6=1) or missing router advertisement; Docker/Kubernetes pods without IPv6 enabled; the chosen NIC has IPv6 but only link-local addresses.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06). Data as JSON: /api/errors/f4dc8a739111abd4. Report an issue: GitHub.