XTLS/Xray-core · error

VLESS users: please use simplified outbound's config style t

Error message

VLESS users: please use simplified outbound's config style to use "reverse"

What it means

Returned by VLessOutboundConfig.Build() when, in full style, the decoded vless.Account carries a non-nil Reverse field. Reverse tunnels are only supported through the simplified outbound config (top-level "reverse" object); the full-style users JSON path is not an accepted way to configure them. The code comment itself notes this branch may be unreachable because vless.Account's Reverse usually does not populate from JSON unmarshal.

Source

Thrown at infra/conf/vless.go:316

				account.Id = c.Id
				account.Flow = c.Flow
				//account.Seed = c.Seed
				account.Encryption = c.Encryption
				if c.Reverse != nil {
					rvs, err := c.Reverse.Build()
					if err != nil {
						return nil, err
					}
					account.Reverse = rvs
				}
				account.Testpre = c.Testpre
				account.Testseed = c.Testseed
			} else {
				if err := json.Unmarshal(rawUser, account); err != nil {
					return nil, errors.New(`VLESS users: invalid user`).Base(err)
				}
				if account.Reverse != nil { // may not be reached: error json unmarshal
					return nil, errors.New(`VLESS users: please use simplified outbound's config style to use "reverse"`)
				}
			}

			u, err := uuid.ParseString(account.Id)
			if err != nil {
				return nil, err
			}
			account.Id = u.String()

			switch account.Flow {
			case "":
			case vless.XRV, vless.XRV + "-udp443":
			default:
				return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
			}

			if !func() bool {
				s := strings.Split(account.Encryption, ".")

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Remove "reverse" from the users entry
  2. Configure reverse via the simplified style: put "reverse":{"tag":"..."} at the outbound top level next to "address"/"id"
  3. Keep the full-style outbound for normal proxying; use a separate simplified outbound for the reverse bridge

Example fix

// before
"settings": { "vnext": [ { "address": "s", "users": [ { "id": "...", "reverse": { "tag": "t" } } ] } ] }
// after (simplified style)
"settings": { "address": "s", "id": "...", "reverse": { "tag": "t" } }
Defensive patterns

Strategy: validation

Validate before calling

func rejectReverseInUsers(cfg map[string]any) error {
	outbounds, _ := cfg["outbounds"].([]any)
	for _, ob := range outbounds {
		m, _ := ob.(map[string]any)
		if p, _ := m["protocol"].(string); p != "vless" { continue }
		settings, _ := m["settings"].(map[string]any)
		if _, simplified := settings["address"]; simplified { continue }
		vnext, _ := settings["vnext"].([]any)
		for _, v := range vnext {
			ep, _ := v.(map[string]any)
			users, _ := ep["users"].([]any)
			for _, u := range users {
				um, _ := u.(map[string]any)
				if _, has := um["reverse"]; has {
					return fmt.Errorf("outbound %v: reverse inside users is not supported; use simplified outbound style", m["tag"])
				}
			}
		}
	}
	return nil
}

Type guard

func userHasReverse(u map[string]any) bool {
	_, ok := u["reverse"]
	return ok
}

Prevention

When it happens

Trigger: Attempting "users":[{"id":"...","reverse":{"tag":"x"}}] in a full-style outbound; practically rare — the account unmarshal typically ignores/skips the reverse field rather than populating it.

Common situations: Experimenting with reverse tunnel configs by inlining reverse into the user object after reading the protobuf definition; hand-crafted JSON aimed at internal field names.

Related errors


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