{"record":{"id":"b54d8e2edf60f639","repo":"XTLS/Xray-core","slug":"vless-reverse-tag-can-t-be-empty","errorCode":null,"errorMessage":"VLESS reverse: \"tag\" can't be empty","messagePattern":"VLESS reverse: \"tag\" can't be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/vless.go","lineNumber":224,"sourceCode":"\t\tif fb.Type == \"\" {\n\t\t\treturn nil, errors.New(`VLESS fallbacks: please fill in a valid value for every \"dest\"`)\n\t\t}\n\t\tif fb.Xver > 2 {\n\t\t\treturn nil, errors.New(`VLESS fallbacks: invalid PROXY protocol version, \"xver\" only accepts 0, 1, 2`)\n\t\t}\n\t}\n\n\treturn config, nil\n}\n\ntype VLessReverseConfig struct {\n\tTag      string          `json:\"tag\"`\n\tSniffing *SniffingConfig `json:\"sniffing\"`\n}\n\nfunc (c *VLessReverseConfig) Build() (*vless.Reverse, error) {\n\tif c.Tag == \"\" {\n\t\treturn nil, errors.New(`VLESS reverse: \"tag\" can't be empty`)\n\t}\n\tr := &vless.Reverse{\n\t\tTag: c.Tag,\n\t}\n\tif c.Sniffing != nil {\n\t\tsc, err := c.Sniffing.Build()\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(`VLESS reverse: invalid \"sniffing\" config`).Base(err)\n\t\t}\n\t\tr.Sniffing = sc\n\t}\n\treturn r, nil\n}\n\ntype VLessOutboundVnext struct {\n\tAddress *Address          `json:\"address\"`\n\tPort    uint16            `json:\"port\"`\n\tUsers   []json.RawMessage `json:\"users\"`","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/vless.go#L206-L242","documentation":"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.","triggerScenarios":"Simplified outbound config with \"reverse\":{} (no \"tag\" key), or reverse: {\"tag\":\"\"}. The error surfaces when VLessOutboundConfig.Build() calls c.Reverse.Build().","commonSituations":"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\").","solutions":["Add a non-empty \"tag\" to the reverse object, e.g. \"reverse\":{\"tag\":\"myreverse\"}","Ensure the tag matches the tag used in routing rules / the server side bridge configuration","Check for typos in the key name — it must be exactly \"tag\""],"exampleFix":"// before\n\"reverse\": { \"sniffing\": { \"enabled\": true } }\n// after\n\"reverse\": { \"tag\": \"reverse_tunnel\", \"sniffing\": { \"enabled\": true } }","handlingStrategy":"validation","validationCode":"func validateReverseTag(cfg map[string]any) error {\n\toutbounds, _ := cfg[\"outbounds\"].([]any)\n\tfor _, ob := range outbounds {\n\t\tm, _ := ob.(map[string]any)\n\t\tsettings, _ := m[\"settings\"].(map[string]any)\n\t\tif rev, ok := settings[\"reverse\"].(map[string]any); ok {\n\t\t\tif t, _ := rev[\"tag\"].(string); t == \"\" {\n\t\t\t\treturn fmt.Errorf(\"outbound %v: reverse.tag is empty\", m[\"tag\"])\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":"func reverseHasTag(rev any) bool {\n\tm, ok := rev.(map[string]any)\n\tif !ok { return false }\n\tt, _ := m[\"tag\"].(string)\n\treturn t != \"\"\n}","tryCatchPattern":null,"preventionTips":["Whenever you add \"reverse\", immediately add its \"tag\" in the same edit","Reuse one naming scheme for reverse tags and reference it in routing rules","Lint outbounds for non-empty reverse.tag before deploy"],"tags":["vless","reverse","config","tag","validation","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}