tailscale/tailscale · error
incompatible SOCKS version
Error message
incompatible SOCKS version
What it means
The first greeting byte (VER) was not 5, so the peer is not speaking SOCKS5 on this connection. Classic causes: a SOCKS4/4a client (VER=4), an HTTP client whose request line starts with 'G' (0x47), or another protocol entirely such as an SSH banner ('S' = 0x53). The server rejects the connection before any method negotiation reply is meaningful.
Source
Thrown at net/socks5/socks5.go:492
portInt, err := strconv.Atoi(portStr)
if err != nil {
return "", 0, err
}
if portInt < 0 || portInt > 65535 {
return "", 0, fmt.Errorf("invalid port number %d", portInt)
}
return host, uint16(portInt), nil
}
// parseClientGreeting parses a request initiation packet.
func parseClientGreeting(r io.Reader, authMethod byte) error {
var hdr [2]byte
_, err := io.ReadFull(r, hdr[:])
if err != nil {
return fmt.Errorf("could not read packet header")
}
if hdr[0] != socks5Version {
return fmt.Errorf("incompatible SOCKS version")
}
count := int(hdr[1])
methods := make([]byte, count)
_, err = io.ReadFull(r, methods)
if err != nil {
return fmt.Errorf("could not read methods")
}
if slices.Contains(methods, authMethod) {
return nil
}
return fmt.Errorf("no acceptable auth methods")
}
func parseClientAuth(r io.Reader) (usr, pwd string, err error) {
var hdr [2]byte
if _, err := io.ReadFull(r, hdr[:]); err != nil {
return "", "", fmt.Errorf("could not read auth packet header")
}View on GitHub (pinned to 57c3357fdb)
Solutions
- Switch the client to SOCKS5 (curl socks5:// or socks5h://, browsers SOCKS5 proxy type, libraries SOCKS5 mode).
- If the client must stay SOCKS4, put a translating proxy in front or extend parseClientGreeting to accept version 4.
- Double-check that the port the client targets is really the SOCKS5 listener.
Example fix
# before curl -x socks4://127.0.0.1:1055 https://example.com # after curl -x socks5h://127.0.0.1:1055 https://example.com
Defensive patterns
Strategy: validation
Validate before calling
// client side: assert SOCKS5 before connecting through a stack that picks the version
if !strings.HasPrefix(scheme, "socks5") {
return fmt.Errorf("refusing to speak %s to a SOCKS5 server", scheme)
} Type guard
func isVersionMismatch(err error) bool {
return strings.Contains(err.Error(), "incompatible SOCKS version")
} Try / catch
if err := conn.Run(); err != nil {
if isVersionMismatch(err) {
s.logf("non-SOCKS5 client %v connected", c.RemoteAddr())
return // expected for misconfigured clients
}
s.logf("client connection failed: %v", err)
} Prevention
- Standardize on socks5h:// URLs so hostnames resolve at the proxy.
- Do not share one listen port between HTTP and SOCKS services.
- Reject SOCKS4-only tooling or front it with a protocol translator.
When it happens
Trigger: curl -x socks4:// pointed at this server; an HTTP request sent straight to the SOCKS port; an HTTPS client connecting without a CONNECT path; a SOCKS client library with a broken version byte; one port accidentally shared by two different proxy services.
Common situations: Misconfigured proxy scheme in browsers or curl (http vs socks4 vs socks5); pointing an HTTP proxy setting at tailscaled's SOCKS5 port; legacy tooling that only speaks SOCKS4.
Related errors
- could not read packet header
- could not read methods
- could not read auth packet header
- bad SOCKS auth version
- error parsing config file %s: no \"version\" field defined
AI-assisted analysis of tailscale/tailscale@57c3357fdb (2026-08-18).
Data as JSON: /api/errors/066e39630981ac1b.
Report an issue: GitHub.