XTLS/Xray-core · error

VMess settings: "vnext" should have one and only one member.

Error message

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

What it means

Thrown by VMessOutboundConfig.Build() when the "vnext" (internally Receivers) array of a full-style VMess outbound does not hold exactly one endpoint. One outbound targets one server; failover/load-balancing across servers is expressed as multiple VMess outbounds plus a routing balancer. The simplified style (top-level "address") auto-synthesizes a single receiver, so only full-style configs with 0 or 2+ entries hit this.

Source

Thrown at infra/conf/vmess.go:137

	Receivers   []*VMessOutboundTarget `json:"vnext"`
}

// Build implements Buildable
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 {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Reduce vnext to exactly one object per outbound
  2. Model multiple servers as multiple tagged outbounds plus a routing balancer with a strategy (e.g. leastPing)
  3. Or switch to simplified style: "address"/"port"/"id" at the outbound settings top level

Example fix

// before
"settings": { "vnext": [ {"address":"a.com","port":443,"users":[...]}, {"address":"b.com","port":443,"users":[...]} ] }
// after
"outbounds": [
  { "protocol": "vmess", "settings": { "vnext": [ {"address":"a.com","port":443,"users":[...]} ] }, "tag": "vm-a" },
  { "protocol": "vmess", "settings": { "vnext": [ {"address":"b.com","port":443,"users":[...]} ] }, "tag": "vm-b" }
]
// plus routing.balancers over ["vm-a","vm-b"]
Defensive patterns

Strategy: validation

Validate before calling

func validateSingleVMessVnext(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)
		if _, simplified := settings["address"]; simplified { continue }
		vnext, _ := settings["vnext"].([]any)
		if len(vnext) != 1 {
			return fmt.Errorf("outbound %v: vmess vnext must have exactly 1 entry, got %d", m["tag"], len(vnext))
		}
	}
	return nil
}

Type guard

func vmessSingleVnext(settings map[string]any) bool {
	if _, ok := settings["address"]; ok { return true }
	v, ok := settings["vnext"].([]any)
	return ok && len(v) == 1
}

Prevention

When it happens

Trigger: "settings":{"vnext":[{...},{...}]} with two servers; "vnext":[] or the key omitted entirely in full style; leftover multi-vnext configs from old V2Ray 3.x.

Common situations: Legacy failover configs predating the balancer requirement; merging two outbounds 'for tidiness'; templates that ship an empty vnext placeholder.

Related errors


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