XTLS/Xray-core · error

VMess vnext: "users" should have one and only one member. Mu

Error message

VMess vnext: "users" should have one and only one member. Multiple members in "users" should use multiple VMess outbounds and routing balancer instead

What it means

Thrown by VMessOutboundConfig.Build() when vnext[].users does not contain exactly one member. Each VMess outbound authenticates as a single user; multiple credentials require separate outbounds (optionally behind a balancer), and an empty users array is likewise invalid.

Source

Thrown at infra/conf/vmess.go:141

func (c *VMessOutboundConfig) Build() (proto.Message, error) {
	errors.PrintNonRemovalDeprecatedFeatureWarning("VMess (with no Forward Secrecy, etc.)", "VLESS Encryption")

	config := new(outbound.Config)
	if c.Address != nil {
		c.Receivers = []*VMessOutboundTarget{
			{
				Address: c.Address,
				Port:    c.Port,
				Users:   []json.RawMessage{{}},
			},
		}
	}
	if len(c.Receivers) != 1 {
		return nil, errors.New(`VMess settings: "vnext" should have one and only one member. Multiple endpoints in "vnext" should use multiple VMess outbounds and routing balancer instead`)
	}
	for _, rec := range c.Receivers {
		if len(rec.Users) != 1 {
			return nil, errors.New(`VMess vnext: "users" should have one and only one member. Multiple members in "users" should use multiple VMess outbounds and routing balancer instead`)
		}
		if rec.Address == nil {
			return nil, errors.New(`VMess vnext: "address" is not set`)
		}
		spec := &protocol.ServerEndpoint{
			Address: rec.Address.Build(),
			Port:    uint32(rec.Port),
		}
		for _, rawUser := range rec.Users {
			user := new(protocol.User)
			if c.Address != nil {
				user.Level = c.Level
				user.Email = c.Email
			} else {
				if err := json.Unmarshal(rawUser, user); err != nil {
					return nil, errors.New("invalid VMess user").Base(err)
				}
			}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Keep exactly one user object: {"id":"uuid","alterId":0,"security":"auto"}
  2. Split multiple identities into multiple outbounds and balance via routing
  3. Never leave "users" empty in an outbound vnext entry

Example fix

// before
"users": [ {"id":"uuid-a"}, {"id":"uuid-b"} ]
// after
"users": [ {"id":"uuid-a", "alterId": 0, "security": "auto"} ]
Defensive patterns

Strategy: validation

Validate before calling

func validateVMessSingleUser(cfg map[string]any) error {
	outbounds, _ := cfg["outbounds"].([]any)
	for _, ob := range outbounds {
		m, _ := ob.(map[string]any)
		if p, _ := m["protocol"].(string); p != "vmess" { continue }
		settings, _ := m["settings"].(map[string]any)
		vnext, _ := settings["vnext"].([]any)
		for _, v := range vnext {
			ep, _ := v.(map[string]any)
			users, _ := ep["users"].([]any)
			if len(users) != 1 {
				return fmt.Errorf("outbound %v: vmess vnext.users must have exactly 1 entry, got %d", m["tag"], len(users))
			}
		}
	}
	return nil
}

Type guard

func vmessOneUser(ep map[string]any) bool {
	u, ok := ep["users"].([]any)
	return ok && len(u) == 1
}

Prevention

When it happens

Trigger: "vnext":[{"address":"a.com","port":443,"users":[]}]; users containing two credential objects; inbound-style clients arrays pasted into the outbound.

Common situations: Copying the server-side clients list into the outbound; removing a user while editing and leaving []; misunderstanding the one-identity-per-outbound rule.

Related errors


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