k3s-io/k3s · error

no IPv6 CIDRs found

Error message

no IPv6 CIDRs found

What it means

getFirst6Net scans a []*net.IPNet and returns the first network whose IP is IPv6 (netutils.IsIPv6); if every entry is IPv4 or nil it returns 'no IPv6 CIDRs found'. It backs the exported GetFirst6String/GetFirst6Net helpers used to pick the IPv6 half of a dual-stack CIDR list. Note the doc comment above the function mistakenly says 'first IPv4 network', but the code strictly filters for IPv6.

Source

Thrown at pkg/util/net.go:105

// If no IPv6 addresses are found, an error is raised.
func getFirst6(elems []net.IP) (net.IP, error) {
	for _, elem := range elems {
		if elem != nil && netutils.IsIPv6(elem) {
			return elem, nil
		}
	}
	return nil, errors.New("no IPv6 address found")
}

// getFirst6Net returns the first IPv4 network from the list of IP networks.
// If no IPv6 addresses are found, an error is raised.
func getFirst6Net(elems []*net.IPNet) (*net.IPNet, error) {
	for _, elem := range elems {
		if elem != nil && netutils.IsIPv6(elem.IP) {
			return elem, nil
		}
	}
	return nil, errors.New("no IPv6 CIDRs found")
}

// GetFirst6String returns the first IPv6 address from a list of IP address strings.
// If no IPv6 addresses are found, an error is raised.
func GetFirst6String(elems []string) (string, error) {
	ips := []net.IP{}
	for _, elem := range elems {
		for _, v := range strings.Split(elem, ",") {
			ips = append(ips, net.ParseIP(v))
		}
	}
	ip, err := getFirst6(ips)
	if err != nil {
		return "", err
	}
	return ip.String(), nil
}

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Ensure the CIDR list actually contains a valid IPv6 network (e.g. fd01::/48) alongside the IPv4 one when dual-stack is intended
  2. Pre-check the list with utilsnet.IsDualStackCIDRs or netutils.IsIPv6CIDR and only call the *6* helper when an IPv6 CIDR is present
  3. If the cluster is intentionally IPv4-only, branch around the IPv6 lookup instead of calling getFirst6Net unconditionally

Example fix

// before
v6, err := util.GetFirst6Net(cidrs) // fails on IPv4-only lists

// after
dual, derr := utilsnet.IsDualStackCIDRs(cidrs)
var v6 *net.IPNet
if derr == nil && dual {
    v6, err = util.GetFirst6Net(cidrs)
} // else: single-stack, skip IPv6 path
Defensive patterns

Strategy: validation

Validate before calling

has6 := false
for _, c := range cidrs {
    if c != nil && netutils.IsIPv6(c.IP) {
        has6 = true
        break
    }
}
if !has6 {
    // single-stack: skip the IPv6 lookup path entirely
}

Type guard

func hasIPv6Net(elems []*net.IPNet) bool {
    for _, e := range elems {
        if e != nil && netutils.IsIPv6(e.IP) {
            return true
        }
    }
    return false
}

Prevention

When it happens

Trigger: Calling GetFirst6Net/GetFirst6String (or getFirst6Net internally) with a list that contains no IPv6 network: a single-stack IPv4 list like [10.42.0.0/16], a list with two IPv4 CIDRs, or nil entries. Typical call sites resolve the IPv6 pod/service CIDR in dual-stack code paths.

Common situations: Dual-stack wiring assumed but cluster-cidr only holds IPv4; server config downgraded to single-stack while caller still asks for the v6 net; earlier parse failures yielding an empty or v4-only slice.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/da6d508b6c1e42b8. Report an issue: GitHub.