thanos-io/thanos · error

lookup IP addresses

Error message

lookup IP addresses %q

What it means

After LookupIPAddr fails on the A/AAAA path, Resolve only swallows the error when the resolver classifies it as NotFound (NXDOMAIN-style). Any other lookup failure is wrapped with "lookup IP addresses <host>" and returned, indicating a real DNS/network problem rather than a missing record.

Solutions

  1. Check DNS server reachability: dig/nslookup the host with the configured nameserver
  2. Inspect the wrapped inner error (errors.Cause) for the root cause (timeout, SERVFAIL, recursion cap)
  3. Fix /etc/resolv.conf or the resolver's ResolvConf path if nameservers are wrong
  4. Retry with backoff for transient network failures; fix CNAME loops in the zone if that is the cause
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := net.LookupHost(host); err != nil {
    var dnsErr *net.DNSError
    if errors.As(err, &dnsErr) && dnsErr.IsNotFound { /* NXDOMAIN: skip, not a network problem */ }
}

Try / catch

res, err := resolver.Resolve(ctx, name, dns.A)
if err != nil {
    cause := errors.Cause(err)
    if errors.Is(cause, miekgdns.ErrNoSuchHost) { /* tolerated: record missing */ }
    if ne, ok := cause.(net.Error); ok && ne.Timeout() { /* retry with backoff */ }
    return err
}

Prevention

When it happens

Trigger: Resolve(ctx, name, A) where the underlying resolver's LookupIPAddr returns a non-NotFound error: DNS server unreachable/timeout, SERVFAIL, network partition, or the iteration-cap/CNAME errors from the miekg resolver.

Common situations: DNS server down or firewalled (port 53 blocked); /etc/resolv.conf pointing at an unresponsive nameserver; SERVFAIL from an upstream zone; CNAME loops triggering the recursion cap during service discovery.

Related errors


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

Appendix: source

Thrown at pkg/discovery/dns/resolver.go:89

	// Split the host and port if present.
	host, port, err := net.SplitHostPort(name)
	if err != nil {
		// The host could be missing a port.
		host, port = name, ""
	}

	switch qtype {
	case A:
		if port == "" {
			return nil, errors.Errorf("missing port in address given for dns lookup: %v", name)
		}
		ips, err := s.resolver.LookupIPAddr(ctx, host)
		if err != nil {
			// We exclude error from std Golang resolver for the case of the domain (e.g `NXDOMAIN`) not being found by DNS
			// server. Since `miekg` does not consider this as an error,  when the host cannot be found, empty slice will be
			// returned.
			if !s.resolver.IsNotFound(err) {
				return nil, errors.Wrapf(err, "lookup IP addresses %q", host)
			}
			if ips == nil {
				level.Error(s.logger).Log("msg", "failed to lookup IP addresses", "host", host, "err", err)
			}
		}
		for _, ip := range ips {
			res = append(res, appendScheme(scheme, net.JoinHostPort(ip.String(), port)))
		}
	case SRV, SRVNoA:
		_, recs, err := s.resolver.LookupSRV(ctx, "", "", host)
		if err != nil {
			if !s.resolver.IsNotFound(err) {
				return nil, errors.Wrapf(err, "lookup SRV records %q", host)
			}
			if len(recs) == 0 {
				level.Error(s.logger).Log("msg", "failed to lookup SRV records", "host", host, "err", err)
			}
		}

View on GitHub (pinned to 35b8b99117)