XTLS/Xray-core · error
failed to add user
Error message
failed to add user
What it means
Server-startup error returned when validator.Add(u) rejects a user during NewServer. In practice the cause is [630]: mixing a non-AEAD cipher with an existing user in a single-port multi-user setup is disallowed because legacy ciphers cannot be distinguished per user on one port. It aborts the whole inbound at startup (AtError).
Source
Thrown at proxy/shadowsocks/server.go:41
type Server struct {
config *ServerConfig
validator *Validator
policyManager policy.Manager
cone bool
}
// NewServer create a new Shadowsocks server.
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
validator := new(Validator)
for _, user := range config.Users {
u, err := user.ToMemoryUser()
if err != nil {
return nil, errors.New("failed to get shadowsocks user").Base(err).AtError()
}
if err := validator.Add(u); err != nil {
return nil, errors.New("failed to add user").Base(err).AtError()
}
}
v := core.MustFromContext(ctx)
s := &Server{
config: config,
validator: validator,
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
cone: ctx.Value("cone").(bool),
}
return s, nil
}
// AddUser implements proxy.UserManager.AddUser().
func (s *Server) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
return s.validator.Add(u)
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Make every user on the single port use an AEAD cipher (aes-*-gcm, *chacha20-poly1305), or give the legacy user its own inbound/port.
- Prefer migrating everyone to shadowsocks-2022, which is designed for multi-user single port.
- Re-run xray run -test to confirm the inbound builds.
Example fix
// before: mixed ciphers on one port
"clients": [
{ "method": "chacha20-poly1305", "password": "a" },
{ "method": "aes-256-cfb", "password": "b" } ]
// after
"clients": [
{ "method": "chacha20-poly1305", "password": "a" },
{ "method": "aes-256-gcm", "password": "b" } ] Defensive patterns
Strategy: validation
Validate before calling
// enforce all-AEAD before adding any user to a shared port
for _, u := range config.Users {
if !isAEAD(u.CipherType) {
return fmt.Errorf("user %q uses non-AEAD cipher; not allowed on multi-user single port", u.Email)
}
} Type guard
func isAEAD(t CipherType) bool {
switch t {
case CipherType_AES_128_GCM, CipherType_AES_256_GCM,
CipherType_CHACHA20_POLY1305, CipherType_XCHACHA20_POLY1305:
return true
}
return false
} Try / catch
if err := validator.Add(u); err != nil {
return fmt.Errorf("cannot add user %q to shared port: %w", u.Email, err)
} Prevention
- Standardize on AEAD (or migrate to shadowsocks-2022) for multi-user ports.
- Test the full user list, not just the newest user, when adding to a shared inbound.
When it happens
Trigger: config.Users contains at least one AEAD user followed by (or preceding) a non-AEAD (legacy stream) user; validator.Add enforces the all-AEAD rule for single-port multi-user and returns an error, which NewServer wraps here.
Common situations: Gradually migrating a shared port from legacy to AEAD ciphers user by user; panels appending an old-cipher user to an existing multi-user inbound.
Related errors
- failed to get shadowsocks user
- unsupported cipher method:
- unknown cipher method:
- Unsupported cipher.
- failed to get cipher
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/84991b344932334e.
Report an issue: GitHub.