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
- Remove "reverse" from the users entry
- Configure reverse via the simplified style: put "reverse":{"tag":"..."} at the outbound top level next to "address"/"id"
- 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
- Configure reverse only via the top-level "reverse" object in a simplified-style outbound
- Do not hand-craft internal protobuf field names inside users JSON
- Keep reverse-tunnel outbounds separate from normal proxy outbounds
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
- VLESS reverse: "tag" can't be empty
- VLESS reverse: invalid "sniffing" config
- VLESS settings: "vnext" should have one and only one member.
- VLESS vnext: "address" is not set
- 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/e120771ceb48bad6.
Report an issue: GitHub.