XTLS/Xray-core · error

SOCKS settings: "servers" should have one and only one membe

Error message

SOCKS settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple SOCKS outbounds and routing balancer instead

What it means

The SOCKS outbound historically allowed a 'servers' array but this implementation restricts it to exactly one member (with a shorthand top-level address form normalized first). Multiple endpoints must instead be modeled as separate SOCKS outbounds plus a routing balancer; zero servers likewise fails.

Source

Thrown at infra/conf/socks.go:101

	Password string               `json:"pass"`
	Servers  []*SocksRemoteConfig `json:"servers"`
}

func (v *SocksClientConfig) Build() (proto.Message, error) {
	config := new(socks.ClientConfig)
	if v.Address != nil {
		v.Servers = []*SocksRemoteConfig{
			{
				Address: v.Address,
				Port:    v.Port,
			},
		}
		if len(v.Username) > 0 {
			v.Servers[0].Users = []json.RawMessage{{}}
		}
	}
	if len(v.Servers) != 1 {
		return nil, errors.New(`SOCKS settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple SOCKS outbounds and routing balancer instead`)
	}
	for _, serverConfig := range v.Servers {
		if len(serverConfig.Users) > 1 {
			return nil, errors.New(`SOCKS servers: "users" should have one member at most. Multiple members in "users" should use multiple SOCKS outbounds and routing balancer instead`)
		}
		server := &protocol.ServerEndpoint{
			Address: serverConfig.Address.Build(),
			Port:    uint32(serverConfig.Port),
		}
		for _, rawUser := range serverConfig.Users {
			user := new(protocol.User)
			if v.Address != nil {
				user.Level = v.Level
				user.Email = v.Email
			} else {
				if err := json.Unmarshal(rawUser, user); err != nil {
					return nil, errors.New("failed to parse Socks user").Base(err).AtError()
				}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Keep exactly one server in 'servers', or use the shorthand form with top-level 'address'/'port'/'users'.
  2. For multiple upstream proxies, define one SOCKS outbound per endpoint and a routing balancer selecting them.
  3. Remove an empty 'servers' array entirely and fill the shorthand fields.

Example fix

// before
"outbounds": [{
  "protocol": "socks",
  "settings": {"servers": [{"address": "a.com", "port": 1080}, {"address": "b.com", "port": 1080}]}
}]

// after
"outbounds": [
  {"tag": "socks-a", "protocol": "socks", "settings": {"servers": [{"address": "a.com", "port": 1080}]}},
  {"tag": "socks-b", "protocol": "socks", "settings": {"servers": [{"address": "b.com", "port": 1080}]}}
],
"routing": {"balancers": [{"tag": "socks-bal", "selector": ["socks-a", "socks-b"]}]}
Defensive patterns

Strategy: validation

Validate before calling

func validSocksOutbound(settings map[string]any) bool {
    servers, ok := settings["servers"].([]any)
    if !ok {
        _, hasAddr := settings["address"]
        return hasAddr
    }
    return len(servers) == 1
}

Prevention

When it happens

Trigger: A SOCKS outbound with settings.servers of length != 1 — an empty array, or two entries like [proxyA, proxyB] — and no top-level 'address' that would have been normalized to one entry.

Common situations: Legacy V2Ray 2.x/3.x configs with multiple SOCKS servers in one outbound; attempting failover by listing several upstream SOCKS proxies; omitting both the shorthand address and servers.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/2c78fd3d0964b76f. Report an issue: GitHub.