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

  1. Convert before calling: pass ip.String() (string) or the net.IP field of the struct you hold
  2. Use the pointer form: pass &tcpAddr, not tcpAddr
  3. For net.IPNet, pass only its .IP field
  4. 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

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


AI-assisted analysis of hashicorp/consul@2397ff0d76 (2026-08-15). Data as JSON: /api/errors/6d820ac5639806df. Report an issue: GitHub.