{"record":{"id":"247dcc6284397fa2","repo":"XTLS/Xray-core","slug":"bridge-tag-is-empty","errorCode":null,"errorMessage":"bridge tag is empty","messagePattern":"bridge tag is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/reverse/bridge.go","lineNumber":31,"sourceCode":"\t\"github.com/xtls/xray-core/features/routing\"\n\t\"github.com/xtls/xray-core/transport\"\n\t\"github.com/xtls/xray-core/transport/pipe\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// Bridge is a component in reverse proxy, that relays connections from Portal to local address.\ntype Bridge struct {\n\tdispatcher  routing.Dispatcher\n\ttag         string\n\tdomain      string\n\tworkers     []*BridgeWorker\n\tmonitorTask *task.Periodic\n}\n\n// NewBridge creates a new Bridge instance.\nfunc NewBridge(config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) {\n\tif config.Tag == \"\" {\n\t\treturn nil, errors.New(\"bridge tag is empty\")\n\t}\n\tif config.Domain == \"\" {\n\t\treturn nil, errors.New(\"bridge domain is empty\")\n\t}\n\n\tb := &Bridge{\n\t\tdispatcher: dispatcher,\n\t\ttag:        config.Tag,\n\t\tdomain:     config.Domain,\n\t}\n\tb.monitorTask = &task.Periodic{\n\t\tExecute:  b.monitor,\n\t\tInterval: time.Second * 2,\n\t}\n\treturn b, nil\n}\n\nfunc (b *Bridge) cleanup() {","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/reverse/bridge.go#L13-L49","documentation":"Reverse-proxy bridge constructor validation: a Bridge (the component on the controlled/inner side that relays connections from the Portal back to local services) requires a tag because that tag is how the reverse instance identifies and routes this bridge. NewBridge returns this error immediately when BridgeConfig.Tag is the empty string, before any worker starts.","triggerScenarios":"Building a reverse config (app/reverse) where a bridges[] entry omits the tag field (or sets it to \"\"), and the instance constructs the Bridge during feature startup.","commonSituations":"Hand-written or generated reverse configs missing the tag on a bridge entry; YAML/JSON field name typo ('Tag' vs 'tag') causing the field to stay empty; trimming a config down for testing and deleting the tag line. Startup fails with this error.","solutions":["Add a unique non-empty \"tag\" to every bridge entry in the reverse config.","Check the bridge tag is not the same as any portal tag or outbound tag it may conflict with in routing.","Validate the reverse config with xray run -test (or run -c config.json -test) before deploying."],"exampleFix":"// before\n\"reverse\": { \"bridges\": [ { \"domain\": \"svc.internal\" } ] }\n\n// after\n\"reverse\": { \"bridges\": [ { \"tag\": \"bridge-1\", \"domain\": \"svc.internal\" } ] }","handlingStrategy":"validation","validationCode":"// Before instance start\nfor _, b := range cfg.Reverse.Bridges {\n    if b.Tag == \"\" { return errors.New(\"reverse config: bridge tag is empty\") }\n}","typeGuard":"func validBridgeConfig(b BridgeConfig) bool { return b.Tag != \"\" }","tryCatchPattern":"// Constructor already validates; catch at feature creation and report path\nif _, err := reverse.NewBridge(cfg, dispatcher); err != nil {\n    if strings.Contains(err.Error(), \"bridge tag is empty\") {\n        return fmt.Errorf(\"reverse.bridges[%d]: tag required\", i)\n    }\n    return err\n}","preventionTips":["Always emit tag+domain together when templating bridge entries","Use config schema validation (JSON schema or -test) before deployment","Keep reverse config pairs (portal/bridge) in one reviewed file"],"tags":["go","xray","reverse-proxy","config","validation"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}