XTLS/Xray-core · error

VLESS reverse: "tag" can't be empty

Error message

VLESS reverse: "tag" can't be empty

What it means

Thrown by VLessReverseConfig.Build() when building a VLESS reverse proxy entry whose "tag" field is empty. The tag is the routing identifier that outbound traffic uses to reach the reverse tunnel, so an empty tag makes the entry unroutable and is rejected immediately.

Source

Thrown at infra/conf/vless.go:224

		if fb.Type == "" {
			return nil, errors.New(`VLESS fallbacks: please fill in a valid value for every "dest"`)
		}
		if fb.Xver > 2 {
			return nil, errors.New(`VLESS fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`)
		}
	}

	return config, nil
}

type VLessReverseConfig struct {
	Tag      string          `json:"tag"`
	Sniffing *SniffingConfig `json:"sniffing"`
}

func (c *VLessReverseConfig) Build() (*vless.Reverse, error) {
	if c.Tag == "" {
		return nil, errors.New(`VLESS reverse: "tag" can't be empty`)
	}
	r := &vless.Reverse{
		Tag: c.Tag,
	}
	if c.Sniffing != nil {
		sc, err := c.Sniffing.Build()
		if err != nil {
			return nil, errors.New(`VLESS reverse: invalid "sniffing" config`).Base(err)
		}
		r.Sniffing = sc
	}
	return r, nil
}

type VLessOutboundVnext struct {
	Address *Address          `json:"address"`
	Port    uint16            `json:"port"`
	Users   []json.RawMessage `json:"users"`

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Add a non-empty "tag" to the reverse object, e.g. "reverse":{"tag":"myreverse"}
  2. Ensure the tag matches the tag used in routing rules / the server side bridge configuration
  3. Check for typos in the key name — it must be exactly "tag"

Example fix

// before
"reverse": { "sniffing": { "enabled": true } }
// after
"reverse": { "tag": "reverse_tunnel", "sniffing": { "enabled": true } }
Defensive patterns

Strategy: validation

Validate before calling

func validateReverseTag(cfg map[string]any) error {
	outbounds, _ := cfg["outbounds"].([]any)
	for _, ob := range outbounds {
		m, _ := ob.(map[string]any)
		settings, _ := m["settings"].(map[string]any)
		if rev, ok := settings["reverse"].(map[string]any); ok {
			if t, _ := rev["tag"].(string); t == "" {
				return fmt.Errorf("outbound %v: reverse.tag is empty", m["tag"])
			}
		}
	}
	return nil
}

Type guard

func reverseHasTag(rev any) bool {
	m, ok := rev.(map[string]any)
	if !ok { return false }
	t, _ := m["tag"].(string)
	return t != ""
}

Prevention

When it happens

Trigger: Simplified outbound config with "reverse":{} (no "tag" key), or reverse: {"tag":""}. The error surfaces when VLessOutboundConfig.Build() calls c.Reverse.Build().

Common situations: Enabling the reverse feature for the first time and forgetting the tag; trimming down a config example too aggressively; tag key misspelled (e.g. "name" or "outboundTag").

Related errors


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