XTLS/Xray-core · error
The cipher is not support Single-port Multi-user
Error message
The cipher is not support Single-port Multi-user
What it means
Policy error from Validator.Add: the account's cipher is not AEAD (IsAEAD() false, i.e. a legacy stream cipher) while the validator already holds at least one user. Single-port multi-user requires AEAD because only AEAD supports reliable per-user discrimination on a shared port; the check is skipped for the first user, so it only fires on the second-and-later non-AEAD add.
Source
Thrown at proxy/shadowsocks/validator.go:34
// Validator stores valid Shadowsocks users.
type Validator struct {
sync.RWMutex
users []*protocol.MemoryUser
behaviorSeed uint64
behaviorFused bool
}
var ErrNotFound = errors.New("Not Found")
// Add a Shadowsocks user.
func (v *Validator) Add(u *protocol.MemoryUser) error {
v.Lock()
defer v.Unlock()
account := u.Account.(*MemoryAccount)
if !account.Cipher.IsAEAD() && len(v.users) > 0 {
return errors.New("The cipher is not support Single-port Multi-user")
}
v.users = append(v.users, u)
if !v.behaviorFused {
hashkdf := hmac.New(sha256.New, []byte("SSBSKDF"))
hashkdf.Write(account.Key)
v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil))
}
return nil
}
// Del a Shadowsocks user with a non-empty Email.
func (v *Validator) Del(email string) error {
if email == "" {
return errors.New("Email must not be empty.")
}
View on GitHub (pinned to 7d214f8b09)
Solutions
- Use AEAD ciphers exclusively on multi-user single-port inbounds.
- Move legacy-cipher users to a dedicated inbound on a separate port.
- Migrate the whole inbound to shadowsocks-2022 for first-class multi-user support.
Example fix
// before
validator.Add(legacyUser) // legacy cipher, users already present -> error
// after
// convert the account first
legacyUser.Account = &MemoryAccount{ CipherType: CipherType_CHACHA20_POLY1305 /* AEAD */, ... } Defensive patterns
Strategy: validation
Validate before calling
func canAddToValidator(v *Validator, acct *MemoryAccount) error {
if !acct.Cipher.IsAEAD() && len(v.users) > 0 {
return errors.New("non-AEAD cipher cannot join an occupied multi-user port")
}
return nil
} Type guard
func isAEADAccount(a *MemoryAccount) bool { return a.Cipher.IsAEAD() } Prevention
- Check IsAEAD() on every account before Validator.Add.
- Remember the rule is order-dependent: first user slips through, second fails — test the pair.
When it happens
Trigger: Calling Validator.Add (directly or via inbound/API AddUser) with a legacy-cipher MemoryAccount when v.users is non-empty; order matters — two legacy users also fail (second one triggers), and legacy-after-AEAD fails.
Common situations: Migrating users onto a shared port incrementally; automation tools adding legacy-cipher users to an existing AEAD inbound; misunderstanding that a single legacy user alone on a port is allowed but mixed setups are not.
Related errors
- Email must not be empty.
- failed to parse user
- Shadowsocks password is not specified.
- unsupported cipher method:
- VLESS vnext: "users" should have one and only one member. Mu
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/1d8c27a714b0a268.
Report an issue: GitHub.