txthinking/brook · error
Invalid dial with IP
Error message
Invalid dial with IP
What it means
NewDialWithIP builds a DialWithIP plugin from an IPv4 and an IPv6 address string. Before error [70] is thrown, the function validates the ip4 argument: it must either be empty or parse as a valid IP via net.ParseIP AND convert to a 4-byte IPv4 address via To4(). This error means the supplied ip4 string was non-empty but was not a valid IPv4 address (it was unparseable, or it was an IPv6/different form), so the constructor aborts rather than storing a bogus address.
Source
Thrown at plugins/dialwithip/dialwithip.go:32
package dialwithip
import (
"errors"
"net"
"github.com/txthinking/brook"
"github.com/txthinking/socks5"
)
type DialWithIP struct {
IP4 net.IP
IP6 net.IP
}
func NewDialWithIP(ip4, ip6 string) (*DialWithIP, error) {
if ip4 != "" && (net.ParseIP(ip4) == nil || net.ParseIP(ip4).To4() == nil) {
return nil, errors.New("Invalid dial with IP")
}
if ip6 != "" && (net.ParseIP(ip6) == nil || net.ParseIP(ip6).To4() != nil) {
return nil, errors.New("Invalid dial with IP")
}
d := &DialWithIP{}
if ip4 != "" {
d.IP4 = net.ParseIP(ip4).To4()
}
if ip6 != "" {
d.IP6 = net.ParseIP(ip6).To16()
}
return d, nil
}
func (p *DialWithIP) TouchBrook() {
brook.DialTCP = func(network string, laddr, raddr string) (net.Conn, error) {
var la, ra *net.TCPAddr
if laddr != "" {View on GitHub (pinned to 5cd13ef3b1)
Solutions
- Check the ip4 argument and correct it to a valid IPv4 address (e.g. "10.0.0.1"); if you only have IPv6, pass "" as ip4 and supply the address as ip6 instead.
- Pre-validate with net.ParseIP(ip4).To4() != nil before calling NewDialWithIP to fail fast with a clearer message.
- If the value is a hostname, resolve it first with net.LookupHost and pick the A record for ip4.
- Trim whitespace and strip any port suffix from the string before passing it in.
Example fix
// before
_, err := brookplugin.NewDialWithIP("example.com", "2001:db8::1")
// after
ip4, _ := net.LookupHost("example.com")
d, err := brookplugin.NewDialWithIP(ip4[0], "2001:db8::1") // ip4[0] e.g. "93.184.216.34" Defensive patterns
Strategy: validation
Validate before calling
func isValidIPv4(s string) bool {
ip := net.ParseIP(strings.TrimSpace(s))
return s == "" || (ip != nil && ip.To4() != nil)
}
// call: if !isValidIPv4(ip4) { return fmt.Errorf("bad ipv4: %q", ip4) } Type guard
func isIPv4(s string) bool {
ip := net.ParseIP(s)
return ip != nil && ip.To4() != nil
} Try / catch
d, err := brookplugin.NewDialWithIP(ip4, ip6)
if err != nil {
if err.Error() == "Invalid dial with IP" {
log.Fatalf("invalid IPv4 literal %q: must parse via net.ParseIP and To4()", ip4)
}
return err
} Prevention
- Always validate IP strings with net.ParseIP + To4()/To16() before passing to constructors.
- Resolve hostnames to literal IPs before use; never pass DNS names where IPs are expected.
- Trim whitespace and strip port/bracket suffixes from user-supplied addresses.
- Keep ip4 and ip6 config fields clearly labeled to avoid argument-order swaps.
When it happens
Trigger: Calling NewDialWithIP with a non-empty ip4 string that fails net.ParseIP (e.g. "999.1.1.1", "192.168.1", "example.com") or that parses but is not IPv4 (e.g. "::1", "2001:db8::1", an IPv4-in-IPv6 form where To4() returns nil).
Common situations: Passing a hostname instead of an IP in config; swapping the arguments and passing an IPv6 address as ip4; typos or extra whitespace/port suffixes like "10.0.0.1:53" in the IPv4 field; environment-specific config files where an IPv6 address was pasted into the IPv4 slot.
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
AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06).
Data as JSON: /api/errors/183e92c9a9538cf7.
Report an issue: GitHub.