XTLS/Xray-core · error
failed to get outbound handler with tag: ${tag}
Error message
failed to get outbound handler with tag: ${tag} What it means
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.
Source
Thrown at app/proxyman/outbound/handler.go:302
Tag: tag,
})) // add another outbound in session ctx
opts := pipe.OptionsFromContext(ctx)
uplinkReader, uplinkWriter := pipe.New(opts...)
downlinkReader, downlinkWriter := pipe.New(opts...)
go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})
conn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader))
if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
conn = tls.Client(conn, tlsConfig)
}
return h.getStatCouterConnection(conn), nil
}
errors.LogError(ctx, "failed to get outbound handler with tag: ", tag)
return nil, errors.New("failed to get outbound handler with tag: " + tag)
}
if h.senderSettings.Via != nil {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
h.SetOutboundGateway(ctx, ob)
}
}
conn, err := internet.Dial(ctx, dest, h.streamSettings)
conn = h.getStatCouterConnection(conn)
outbounds := session.OutboundsFromContext(ctx)
if outbounds != nil {
ob := outbounds[len(outbounds)-1]
ob.Conn = conn
} else {
// for Vision's pre-connect
}View on GitHub (pinned to 7d214f8b09)
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.
Example fix
// before
{ "tag": "chain", "protocol": "freedom",
"settings": { "domainStrategy": "UseIP" },
"streamSettings": { "sockopt": { }, "proxySettings": { "tag": "prox" } } }
// after
{ "tag": "chain", "protocol": "freedom",
"settings": { "domainStrategy": "UseIP" },
"streamSettings": { "proxySettings": { "tag": "proxy" } } }
// matching an outbound defined as: { "tag": "proxy", "protocol": "vless", ... } Defensive patterns
Strategy: validation
Validate before calling
// Verify tag exists before dispatch/dial relies on it
ohm := feature.GetOutboundManager(ctx) // app/proxyman/outbound Manager
if h, ok := ohm.GetHandler(chainedTag); !ok || h == nil {
return fmt.Errorf("config error: chained outbound tag %q undefined", chainedTag)
} Type guard
func hasOutboundTag(ohm outbound.Manager, tag string) bool {
h, ok := ohm.GetHandler(tag)
return ok && h != nil
} Try / catch
// Fail loudly at config-load time, not per-connection at runtime
for _, ob := range cfg.Outbounds {
if t := streamProxyTag(ob); t != "" && !definedTags[t] {
return fmt.Errorf("outbound %q references undefined tag %q", ob.Tag, t)
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- existing tag found: ${tag}
- unsupported domain strategy: {}
- invalid redirect address: {}
- invalid redirect port: {}
- SOCKS servers: "users" should have one member at most. Multi
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/c9f560ed28896f04.
Report an issue: GitHub.