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
- Enable global IPv6 on the host/interface (check `ip -6 addr`, router advertisements, SLAAC/DHCPv6) so a global unicast address exists.
- If IPv6 is unavailable, configure the plugin for IPv4 instead (pass v46="4") or use the plain dialer without a source-NIC constraint.
- Inspect interface addresses with net.InterfaceAddrs() to confirm which families are present before selecting v46.
- 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
- Probe net.InterfaceAddrs() for a global unicast IPv6 address before requesting IPv6 dialing.
- Ensure hosts/containers have IPv6 enabled (sysctl net.ipv6.conf.all.disable_ipv6=0, RA/SLAAC working).
- Treat this error as 'no IPv6 connectivity' and design callers to degrade to IPv4 (Happy Eyeballs style).
- In Kubernetes/Docker, enable IPv6 in the network stack if IPv6 egress is required.
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.