{"record":{"id":"c9f560ed28896f04","repo":"XTLS/Xray-core","slug":"failed-to-get-outbound-handler-with-tag-tag","errorCode":null,"errorMessage":"failed to get outbound handler with tag: ${tag}","messagePattern":"failed to get outbound handler with tag: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/proxyman/outbound/handler.go","lineNumber":302,"sourceCode":"\t\t\t\t\tTag:    tag,\n\t\t\t\t})) // add another outbound in session ctx\n\t\t\t\topts := pipe.OptionsFromContext(ctx)\n\t\t\t\tuplinkReader, uplinkWriter := pipe.New(opts...)\n\t\t\t\tdownlinkReader, downlinkWriter := pipe.New(opts...)\n\n\t\t\t\tgo handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})\n\t\t\t\tconn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader))\n\n\t\t\t\tif config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {\n\t\t\t\t\ttlsConfig := config.GetTLSConfig(tls.WithDestination(dest))\n\t\t\t\t\tconn = tls.Client(conn, tlsConfig)\n\t\t\t\t}\n\n\t\t\t\treturn h.getStatCouterConnection(conn), nil\n\t\t\t}\n\n\t\t\terrors.LogError(ctx, \"failed to get outbound handler with tag: \", tag)\n\t\t\treturn nil, errors.New(\"failed to get outbound handler with tag: \" + tag)\n\t\t}\n\n\t\tif h.senderSettings.Via != nil {\n\t\t\toutbounds := session.OutboundsFromContext(ctx)\n\t\t\tob := outbounds[len(outbounds)-1]\n\t\t\th.SetOutboundGateway(ctx, ob)\n\t\t}\n\t}\n\n\tconn, err := internet.Dial(ctx, dest, h.streamSettings)\n\tconn = h.getStatCouterConnection(conn)\n\toutbounds := session.OutboundsFromContext(ctx)\n\tif outbounds != nil {\n\t\tob := outbounds[len(outbounds)-1]\n\t\tob.Conn = conn\n\t} else {\n\t\t// for Vision's pre-connect\n\t}","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/proxyman/outbound/handler.go#L284-L320","documentation":"Raised in the handler's connection dialer when the outbound is configured to dial through another handler identified by tag (e.g. a chained outbound using proxySettings.tag, optionally wrapped in TLS from streamSettings), and no registered outbound handler carries that tag. The lookup fails, the error is logged, and dialing aborts — the fallback is to dial directly only in the success path, which is never reached here.","triggerScenarios":"Handler config with proxySettings (or streamSettings-based chaining) whose tag string does not match any handler registered in the outbound manager — for example tag 'proxy' vs registered 'outbound-proxy', a handler removed/renamed, or a tag defined only on the server side. The code path builds an internal pipe connection and dispatches through the resolved handler; resolution failing triggers the error before internet.Dial.","commonSituations":"Renaming an outbound in config but forgetting to update references in another outbound's proxySettings.tag or in routing balancer rules; typo in the tag; referencing a tag that is created later/dynamically; exporting a template that omits the referenced outbound.","solutions":["Check the exact spelling/case of the tag in proxySettings against the \"tag\" field of the target outbound in config.","Ensure the referenced outbound is actually defined in the outbounds array (and not removed by a GUI/ template collapse).","Prefer using routing rules/balancers with existing tags instead of manual chaining, or define the tag on both ends consistently.","After fixing, restart and confirm with loglevel info that no 'failed to get outbound handler' lines appear."],"exampleFix":"// before\n{ \"tag\": \"chain\", \"protocol\": \"freedom\",\n  \"settings\": { \"domainStrategy\": \"UseIP\" },\n  \"streamSettings\": { \"sockopt\": { }, \"proxySettings\": { \"tag\": \"prox\" } } }\n\n// after\n{ \"tag\": \"chain\", \"protocol\": \"freedom\",\n  \"settings\": { \"domainStrategy\": \"UseIP\" },\n  \"streamSettings\": { \"proxySettings\": { \"tag\": \"proxy\" } } }\n// matching an outbound defined as: { \"tag\": \"proxy\", \"protocol\": \"vless\", ... }","handlingStrategy":"validation","validationCode":"// Verify tag exists before dispatch/dial relies on it\nohm := feature.GetOutboundManager(ctx) // app/proxyman/outbound Manager\nif h, ok := ohm.GetHandler(chainedTag); !ok || h == nil {\n    return fmt.Errorf(\"config error: chained outbound tag %q undefined\", chainedTag)\n}","typeGuard":"func hasOutboundTag(ohm outbound.Manager, tag string) bool {\n    h, ok := ohm.GetHandler(tag)\n    return ok && h != nil\n}","tryCatchPattern":"// Fail loudly at config-load time, not per-connection at runtime\nfor _, ob := range cfg.Outbounds {\n    if t := streamProxyTag(ob); t != \"\" && !definedTags[t] {\n        return fmt.Errorf(\"outbound %q references undefined tag %q\", ob.Tag, t)\n    }\n}","preventionTips":["Lint configs: every proxySettings.tag / balancerTag must exist in outbounds","Rename tags with a project-wide search, not by hand","Prefer routing rules over manual proxySettings chaining where possible","Run xray run -test on config changes in CI"],"tags":["go","xray","outbound","tag","config"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}