grpc/grpc-go · warning
dns: %v record lookup error: %v
Error message
dns: %v record lookup error: %v
What it means
Logged and returned by handleDNSError for any DNS lookup error that is not a benign name-not-found (i.e. a timeout or temporary error). Benign non-existent-name errors are suppressed (returned as nil); genuine timeouts, temporary failures, or server errors are wrapped with the lookup type and propagated so the resolver backs off and retries. It is logged at Info level, not fatal.
Source
Thrown at internal/resolver/dns/dns_resolver.go:287
return nil, fmt.Errorf("dns: error parsing A record IP address %v: %v", a, err)
}
addr := ip + ":" + strconv.Itoa(int(s.Port))
newAddrs = append(newAddrs, resolver.Address{Addr: addr, ServerName: s.Target})
}
}
return newAddrs, nil
}
func handleDNSError(err error, lookupType string) error {
dnsErr, ok := err.(*net.DNSError)
if ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {
// Timeouts and temporary errors should be communicated to gRPC to
// attempt another DNS query (with backoff). Other errors should be
// suppressed (they may represent the absence of a TXT record).
return nil
}
if err != nil {
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
logger.Info(err)
}
return err
}
func (d *dnsResolver) lookupTXT(ctx context.Context) *serviceconfig.ParseResult {
ss, err := d.resolver.LookupTXT(ctx, txtPrefix+d.host)
if err != nil {
if envconfig.TXTErrIgnore {
return nil
}
if err = handleDNSError(err, "TXT"); err != nil {
return &serviceconfig.ParseResult{Err: err}
}
return nil
}
var res string
for _, s := range ss {View on GitHub (pinned to 03255a9237)
Solutions
- Verify nameservers in /etc/resolv.conf are reachable (dig @ns host).
- Increase DNS timeouts or use a custom resolver via the dns://authority/ target.
- Treat as transient: the dns resolver already retries with exponential backoff.
- Check GRPC_DNS_RESOLVER / custom authority configuration.
Example fix
// before: default resolver times out
conn, _ := grpc.Dial("dns:///svc:443", ...)
// after: pin a reliable authority
conn, _ := grpc.Dial("dns:///8.8.8.8/svc:443", ...) Defensive patterns
Strategy: retry
Validate before calling
// Health-check /etc/resolv.conf nameservers at startup.
func dnsReachable() error {
r := &net.Resolver{PreferGo: true}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err := r.LookupHost(ctx, "dns.google")
return err
} Try / catch
// The dns resolver already retries with exponential backoff.
// Surface it as a metric; only act if it persists beyond a threshold.
if status.Code(err) == codes.DeadlineExceeded {
metricDNSFailures.Inc()
} Prevention
- Point /etc/resolv.conf at a reliable, low-latency nameserver.
- Use the dns://authority/ form to pin a known-good resolver.
- Raise DNS timeouts in constrained environments.
When it happens
Trigger: DNS server unreachable, query timeout, network partition, or rate limiting during A/SRV/TXT lookups. Also a misconfigured custom DNS authority (dns://authority/host) that refuses queries.
Common situations: Container /etc/resolv.conf pointing at a slow or dead nameserver; intermittent network issues; systemd-resolved returning SERVFAIL; very short DNS timeouts in embedded environments.
Related errors
- delegating_resolver: unable to build the proxy resolver: %v
- dns resolver: missing address
- dns resolver: missing port after port-separator colon
- delegating_resolver: invalid target address %q: %v
- delegating_resolver: unable to build the resolver for target
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/6d0f00b5583a8c1d.
Report an issue: GitHub.