thanos-io/thanos · error

exchange

Error message

exchange

What it means

Wraps any failure of dns.Client.Exchange — the actual network query to a DNS server. Everything from socket errors and timeouts to I/O failures during the request/response round-trip surfaces here with the original error attached.

Solutions

  1. Read the wrapped underlying error (e.g. 'i/o timeout', 'connection refused') to identify the transport problem
  2. Increase the Resolver timeout if slow resolvers are legitimately in use
  3. Verify UDP/TCP 53 connectivity to servAddr with dig @servAddr
  4. Fix routing/firewall/NAT issues between the client and the DNS server
Defensive patterns

Strategy: retry

Validate before calling

// sanity-check reachability and latency before lookups
t0 := time.Now(); err := probeDNS(server, name); if time.Since(t0) > timeout { /* resolver too slow */ }

Try / catch

if err != nil && strings.Contains(err.Error(), "exchange") {
    if errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "i/o timeout") {
        return retryWithBackoff()
    }
    return err
}

Prevention

When it happens

Trigger: client.Exchange(msg, servAddr) fails: UDP/TCP timeout (default or configured timeout exceeded), network unreachable, connection refused/reset, or short/invalid response read.

Common situations: DNS server down or overloaded; packet loss on the network; queries dropped by egress network policy; resolv.conf pointing at a non-DNS host; timeouts set too low for slow resolvers.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/aa2e08cc06ff6c2e. Report an issue: GitHub.

Appendix: source

Thrown at pkg/discovery/dns/miekgdns/lookup.go:140

	}
	return b.String()
}

// askServerForName makes a request to a specific DNS server for a specific
// name (and qtype). Retries with TCP in the event of response truncation,
// but otherwise just sends back whatever the server gave, whether that be a
// valid-looking response, or an error.
func askServerForName(name string, qType dns.Type, client *dns.Client, servAddr string, edns bool) (*dns.Msg, error) {
	msg := &dns.Msg{}

	msg.SetQuestion(dns.Fqdn(name), uint16(qType))
	if edns {
		msg.SetEdns0(dns.DefaultMsgSize, false)
	}

	response, _, err := client.Exchange(msg, servAddr)
	if err != nil {
		return nil, errors.Wrapf(err, "exchange")
	}

	if response.Truncated {
		if client.Net == "tcp" {
			return nil, errors.New("got truncated message on TCP (64kiB limit exceeded?)")
		}

		// TCP fallback.
		client.Net = "tcp"
		return askServerForName(name, qType, client, servAddr, false)
	}

	return response, nil
}

View on GitHub (pinned to 35b8b99117)