XTLS/Xray-core · error

shadowsocks 2022 (relay): users must have empty method

Error message

shadowsocks 2022 (relay): users must have empty method

What it means

In Shadowsocks-2022 relay mode (users have addresses), relay destinations share the server-level method for key derivation, so individual user cipher entries are rejected. This is the relay counterpart of the multi-user 'empty method' rule.

Source

Thrown at infra/conf/shadowsocks.go:164

				Email:   user.Email,
				Level:   uint32(user.Level),
				Account: serial.ToTypedMessage(account),
			}
			return nil
		}
		if err := task.ParallelForN(len(v.Users), processUser); err != nil {
			return nil, err
		}
		return config, nil
	}

	config := new(shadowsocks_2022.RelayServerConfig)
	config.Method = v.Cipher
	config.Key = v.Password
	config.Network = v.NetworkList.Build()
	for _, user := range v.Users {
		if user.Cipher != "" {
			return nil, errors.New("shadowsocks 2022 (relay): users must have empty method")
		}
		if user.Address == nil {
			return nil, errors.New("shadowsocks 2022 (relay): all users must have relay address")
		}
		config.Destinations = append(config.Destinations, &shadowsocks_2022.RelayDestination{
			Key:     user.Password,
			Email:   user.Email,
			Address: user.Address.Build(),
			Port:    uint32(user.Port),
		})
	}
	return config, nil
}

type ShadowsocksServerTarget struct {
	Address  *Address `json:"address"`
	Port     uint16   `json:"port"`
	Level    byte     `json:"level"`

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Delete the per-user 'cipher' field from every relay destination entry.
  2. Keep only 'password', 'address', 'port', 'email', 'level' on each user; the method lives solely on the server.

Example fix

// before
"users": [{"cipher": "2022-blake3-aes-256-gcm", "address": "next-hop", "port": 8388, "password": "..."}]

// after
"users": [{"address": "next-hop", "port": 8388, "password": "..."}]
Defensive patterns

Strategy: validation

Validate before calling

func validRelayUsers(users []User) bool {
    for _, u := range users {
        if u.Cipher != "" {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: A 2022 relay inbound where any destination user in 'users' carries a non-empty 'cipher' field.

Common situations: Copying multi-user configs into relay setups and leaving per-user methods; template generators that populate all fields.

Related errors


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