hashicorp/consul · error
invalid type: %T
Error message
invalid type: %T
What it means
The unexported iptos helper in ipaddr/ipaddr.go:60 panics for any type outside its type switch: string, *string, net.IP, *net.IP, *net.IPAddr, *net.TCPAddr, *net.UDPAddr. It backs the public helpers IsAny, IsAnyV4 and IsAnyV6, which accept interface{} precisely so several address representations can be passed. Note the asymmetry: net.IP is accepted by value, but IPAddr/TCPAddr/UDPAddr are only accepted as pointers, and net.IPNet or custom Stringer types are not accepted at all.
Source
Thrown at ipaddr/ipaddr.go:60
case string:
return x
case *string:
if x == nil {
return ""
}
return *x
case net.IP:
return x.String()
case *net.IP:
return x.String()
case *net.IPAddr:
return x.IP.String()
case *net.TCPAddr:
return x.IP.String()
case *net.UDPAddr:
return x.IP.String()
default:
panic(fmt.Sprintf("invalid type: %T", ip))
}
}
View on GitHub (pinned to 2397ff0d76)
Solutions
- Convert before calling: pass ip.String() (string) or the net.IP field of the struct you hold
- Use the pointer form: pass &tcpAddr, not tcpAddr
- For net.IPNet, pass only its .IP field
- If a type is broadly needed, extend iptos with a new case (contribute upstream)
Example fix
// before
addr := net.TCPAddr{IP: net.ParseIP("0.0.0.0")}
ipaddr.IsAny(addr) // value net.TCPAddr not in switch -> panics
// after
ipaddr.IsAny(&addr) // *net.TCPAddr is supported
// or
ipaddr.IsAny("0.0.0.0") // plain string is supported Defensive patterns
Strategy: type-guard
Validate before calling
func toAddrString(v any) (string, bool) {
switch x := v.(type) {
case string:
return x, true
case *string:
if x == nil {
return "", true
}
return *x, true
case net.IP:
return x.String(), true
case *net.IP:
return x.String(), true
case *net.IPAddr:
return x.IP.String(), true
case *net.TCPAddr:
return x.IP.String(), true
case *net.UDPAddr:
return x.IP.String(), true
}
return "", false
}
if s, ok := toAddrString(addr); ok {
_ = ipaddr.IsAny(s)
} Type guard
func supportedAddrType(v any) bool {
switch v.(type) {
case string, *string, net.IP, *net.IP, *net.IPAddr, *net.TCPAddr, *net.UDPAddr:
return true
}
return false
} Try / catch
func safeIsAny(v any) (b bool, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ipaddr: unsupported address type: %v", r)
}
}()
return ipaddr.IsAny(v), nil
} Prevention
- Prefer passing a plain string or net.IP into ipaddr helpers; both are always in the switch
- Watch the value/pointer asymmetry: TCPAddr/UDPAddr/IPAddr must be pointers, net.IP must be a value
- For net.IPNet pass .IP; for custom Stringer types call .String() yourself
When it happens
Trigger: Calling ipaddr.IsAny with a value (non-pointer) net.TCPAddr or net.UDPAddr; passing a net.IPNet (e.g. from parsing a CIDR); passing a custom type that merely implements fmt.Stringer; passing *net.TCPListener or other socket types.
Common situations: Refactoring address plumbing so a value that used to be *net.TCPAddr is now passed by value; feeding a subnet (net.IPNet) instead of its .IP field; assuming any Stringer works because the parameter is interface{}.
Related errors
- unable to convert %T to proto
- ClusterSize not set
- failed to decode request: %v
- failed to decode batch updates: %v
- all indexers must have a non-empty name
AI-assisted analysis of hashicorp/consul@2397ff0d76 (2026-08-15).
Data as JSON: /api/errors/6d820ac5639806df.
Report an issue: GitHub.