cloudflare/cloudflared · error
expect IPv6, but %s is IPv4
Error message
expect IPv6, but %s is IPv4
What it means
determineICMPv6Src resolves the local IPv6 source address (and zone) used for ICMPv6 echo traffic. When a user-supplied source address parses as IPv4 instead of IPv6, `addr.Is6()` fails and this error is returned. The address and its IPv6 zone are both required to bind ICMPv6 sockets correctly.
Source
Thrown at cmd/cloudflared/tunnel/configuration.go:425
}
return addr, nil
}
type interfaceIP struct {
name string
ip net.IP
}
func determineICMPv6Src(userDefinedSrc string, logger *zerolog.Logger, ipv4Src netip.Addr) (addr netip.Addr, zone string, err error) {
if userDefinedSrc != "" {
addr, err := netip.ParseAddr(userDefinedSrc)
if err != nil {
return netip.Addr{}, "", err
}
if addr.Is6() {
return addr, addr.Zone(), nil
}
return netip.Addr{}, "", fmt.Errorf("expect IPv6, but %s is IPv4", userDefinedSrc)
}
// Loop through all the interfaces, the preference is
// 1. The interface where ipv4Src is in
// 2. Interface with IPv6 address
// 3. Unspecified interface
interfaces, err := net.Interfaces()
if err != nil {
return netip.IPv6Unspecified(), "", nil
}
interfacesWithIPv6 := make([]interfaceIP, 0)
for _, interf := range interfaces {
interfaceAddrs, err := interf.Addrs()
if err != nil {
continue
}View on GitHub (pinned to 2253eeeb25)
Solutions
- Set the ICMPv6 source to a valid IPv6 address, including a zone if link-local (e.g. fe80::1%eth0)
- Remove the explicit source so cloudflared discovers an IPv6 address automatically
- If you intended IPv4, use the ICMPv4 source field instead
Example fix
// before $ cloudflared tunnel run --icmpv6-src 192.168.0.1 // after $ cloudflared tunnel run --icmpv6-src fe80::1%eth0
Defensive patterns
Strategy: validation
Validate before calling
addr, err := netip.ParseAddr(src)
if err != nil || !addr.Is6() {
return fmt.Errorf("--icmpv6-src must be an IPv6 address, got %q", src)
}
if addr.IsLinkLocalUnicast() && addr.Zone() == "" {
return fmt.Errorf("link-local IPv6 source %q requires a zone, e.g. %q", src, src+"%eth0")
} Type guard
func isIPv6Literal(s string) bool { a, err := netip.ParseAddr(s); return err == nil && a.Is6() } Try / catch
if _, _, err := determineICMPv6Src(c); err != nil {
logger.Error().Err(err).Msg("invalid ICMPv6 source")
os.Exit(1)
} Prevention
- Omit the ICMPv6 source flag when possible; auto-discovery picks a working address and zone
- Always include a zone for link-local addresses in configs
- Differentiate v4/v6 template variables explicitly in provisioning scripts
When it happens
Trigger: Setting the ICMPv6 source (e.g. `--icmpv6-src`) to an IPv4 literal like `192.168.0.1`, so `addr.Is6()` returns false after parsing the user-defined address.
Common situations: Copy-paste of the ICMPv4 source into the v6 flag; stale config files written before moving to dual-stack; automation templates parameterized with an IPv4 default.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- expect IPv4, but %s is IPv6
- IPv6 bind address is specified, but edge-ip-version is IPv4
- failed to determine IPv6 source address for ICMP proxy
- funnel not found
- Invalid IP %s
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/11a4a1d77c7051b3.
Report an issue: GitHub.