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

  1. Use AEAD ciphers exclusively on multi-user single-port inbounds.
  2. Move legacy-cipher users to a dedicated inbound on a separate port.
  3. 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

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


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