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

  1. Switch the client to SOCKS5 (curl socks5:// or socks5h://, browsers SOCKS5 proxy type, libraries SOCKS5 mode).
  2. If the client must stay SOCKS4, put a translating proxy in front or extend parseClientGreeting to accept version 4.
  3. 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

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


AI-assisted analysis of tailscale/tailscale@57c3357fdb (2026-08-18). Data as JSON: /api/errors/066e39630981ac1b. Report an issue: GitHub.