v2rayA/v2rayA · error

dns upstream: invalid response: %w

Error message

dns upstream: invalid response: %w

What it means

exchangeDirect validates the DNS message received from a direct upstream with ValidateResponse and wraps any failure as "dns upstream: invalid response: %w". The reply arrived and parsed as DNS, but it is not an acceptable response — e.g. it is not a response to a query (QR bit unset), is malformed, or carries an unexpected opcode. This guards against spoofed or broken upstreams.

Source

Thrown at core/dns/upstream_stub.go:161

				log.Printf("[dns upstream] TCP fallback error (attempt %d): %s %s → %s: %v", attempt+1,
					dns.Type(uint16(query.QType)).String(), query.Name, upstream.Addr, err)
				continue
			}
		}

		break
	}

	if err != nil {
		return nil, fmt.Errorf("dns upstream: exchange failed: %w", err)
	}

	if resp == nil {
		return nil, nil
	}

	if err := ValidateResponse(resp); err != nil {
		return nil, fmt.Errorf("dns upstream: invalid response: %w", err)
	}
	if err := ValidateQuestionMatch(resp, query.Name, query.QType); err != nil {
		return nil, fmt.Errorf("dns upstream: question mismatch: %w", err)
	}

	var ttl uint32
	if len(resp.Answer) > 0 {
		ttl = resp.Answer[0].Header().Ttl
		for _, rr := range resp.Answer[1:] {
			if rr.Header().Ttl < ttl {
				ttl = rr.Header().Ttl
			}
		}
	}

	dnsResp := &DnsResponse{
		Query:      *query,
		RawMsg:     resp,

View on GitHub (pinned to 71e5442fc5)

Solutions

  1. Inspect resp.Rcode/flags via the log output or capture with tcpdump to see what the upstream actually returned.
  2. Switch to a trusted upstream (1.1.1.1, 8.8.8.8) or an encrypted one (DoH/DoT) to bypass port-53 interception.
  3. Complete captive-portal authentication or leave the intercepting network.
  4. Mark the failing upstream unhealthy in config and let a fallback upstream serve queries.
  5. Check whether a local firewall/NAT device is rewriting UDP/53 payloads.

Example fix

// before
upstream addr: "192.168.1.1:53" // router hijacked by ISP, returns junk
// after
upstream addr: "1.1.1.1:53" // or enable DoH upstream to avoid interception
Defensive patterns

Strategy: retry

Validate before calling

// Sanity-check the upstream before use: a valid reply should parse and be a response
q := new(dns.Msg)
q.SetQuestion(dns.Fqdn("example.com."), dns.TypeA)
c := new(dns.Client)
r, _, err := c.Exchange(q, upstream.Addr)
if err != nil || r == nil || !r.Response || r.Rcode == dns.RcodeFormatError {
    return fmt.Errorf("upstream %s returns invalid responses", upstream.Addr)
}

Type guard

func isInvalidResponseErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "dns upstream: invalid response")
}

Try / catch

resp, err := mgr.Exchange(upstream, query)
if err != nil {
    var target *dns.Msg
    if strings.Contains(err.Error(), "invalid response") {
        log.Printf("upstream %s returned invalid reply, failing over: %v", upstream.Addr, err)
        resp, err = mgr.Exchange(fallbackUpstream, query)
    }
    _ = target
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling UpstreamManager.Exchange on a direct upstream whose reply fails ValidateResponse: responses with QR=0, malformed flags/sections, or otherwise structurally invalid messages returned by the server or injected by a middlebox (captive portals, ISP hijackers).

Common situations: Captive portal or hotel/airport Wi-Fi intercepting port 53 and returning an HTML/redirect page or junk packet; a middlebox rewriting DNS payloads; a misconfigured upstream replying with queries instead of responses; spoofed answers on untrusted networks.

Related errors


AI-assisted analysis of v2rayA/v2rayA@71e5442fc5 (2026-09-05). Data as JSON: /api/errors/5785eb90ad55f78e. Report an issue: GitHub.