XTLS/Xray-core · error · errors.Error

invalid outbound

Error message

invalid outbound

What it means

The DNS outbound handler (proxy/dns) Process() requires the current session's last outbound to carry a valid target, because it rewrites and forwards that target as the DNS server destination. If ob.Target is zero (network unknown AND address nil) the handler cannot function and returns this error immediately, before any I/O.

Source

Thrown at proxy/dns/dns.go:159

func (h *Handler) applyRules(qType dnsmessage.Type, domain string) (RuleAction, dnsmessage.RCode) {
	qCode := uint16(qType)
	for _, r := range h.rules {
		if r.Apply(qCode, domain) {
			return r.action, r.rCode
		}
	}
	if qType == dnsmessage.TypeA || qType == dnsmessage.TypeAAAA {
		return RuleAction_Hijack, dnsmessage.RCodeSuccess
	}
	return RuleAction_Return, dnsmessage.RCodeSuccess
}

// Process implements proxy.Outbound.
func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.Dialer) error {
	outbounds := session.OutboundsFromContext(ctx)
	ob := outbounds[len(outbounds)-1]
	if !ob.Target.IsValid() {
		return errors.New("invalid outbound")
	}
	ob.Name = "dns"

	srcNetwork := ob.Target.Network

	dest := ob.Target
	if h.rewriteServer.Network != net.Network_Unknown {
		dest.Network = h.rewriteServer.Network
	}
	if h.rewriteServer.Address != nil {
		dest.Address = h.rewriteServer.Address
	}
	if h.rewriteServer.Port != 0 {
		dest.Port = h.rewriteServer.Port
	}

	errors.LogInfo(ctx, "handling DNS traffic to ", dest)

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Restrict the routing rule that targets the dns outbound to DNS traffic only (network: udp, port: 53)
  2. Never set the dns outbound as the first/default outbound; keep freedom/blackhole first
  3. Verify the paired inbound actually resolves original destination (see error 558 for dokodemo)
  4. If you need a plain DNS forwarder instead, configure a DNS module rather than routing raw traffic to the dns outbound

Example fix

// before
{ "type":"field", "outboundTag":"dns-out", "network":"tcp,udp" }

// after
{ "type":"field", "outboundTag":"dns-out", "network":"udp", "port": 53 }
Defensive patterns

Strategy: validation

Validate before calling

// in routing-rule generation, assert dns outbound is only matched by DNS queries
func ruleIsDNSOnly(r Rule) bool { return r.OutboundTag == "dns-out" && r.Network == "udp" && r.Port == 53 }

Type guard

func hasValidTarget(ctx context.Context) bool {
    obs := session.OutboundsFromContext(ctx)
    if len(obs) == 0 { return false }
    t := obs[len(obs)-1].Target
    return t.IsValid() && t.Address != nil
}

Try / catch

if err := dnsHandler.Process(ctx, link, d); err != nil {
    if err.Error() == "invalid outbound" { skipAndLog(ctx); return nil }
    return err
}

Prevention

When it happens

Trigger: Routing non-DNS or target-less traffic to the "dns" outbound: e.g. a routing rule with outboundTag: dns matching a stream whose destination was not captured, or using the dns outbound as the default outbound for traffic lacking a destination.

Common situations: Copy-pasted routing configs where rules send all traffic (not just UDP port 53) to the dns outbound; using dns outbound with inbound types that do not produce a valid original-destination.

Related errors


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