{"record":{"id":"6d0f00b5583a8c1d","repo":"grpc/grpc-go","slug":"dns-v-record-lookup-error-v","errorCode":null,"errorMessage":"dns: %v record lookup error: %v","messagePattern":"dns: (.+?) record lookup error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/resolver/dns/dns_resolver.go","lineNumber":287,"sourceCode":"\t\t\t\treturn nil, fmt.Errorf(\"dns: error parsing A record IP address %v: %v\", a, err)\n\t\t\t}\n\t\t\taddr := ip + \":\" + strconv.Itoa(int(s.Port))\n\t\t\tnewAddrs = append(newAddrs, resolver.Address{Addr: addr, ServerName: s.Target})\n\t\t}\n\t}\n\treturn newAddrs, nil\n}\n\nfunc handleDNSError(err error, lookupType string) error {\n\tdnsErr, ok := err.(*net.DNSError)\n\tif ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {\n\t\t// Timeouts and temporary errors should be communicated to gRPC to\n\t\t// attempt another DNS query (with backoff).  Other errors should be\n\t\t// suppressed (they may represent the absence of a TXT record).\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\terr = fmt.Errorf(\"dns: %v record lookup error: %v\", lookupType, err)\n\t\tlogger.Info(err)\n\t}\n\treturn err\n}\n\nfunc (d *dnsResolver) lookupTXT(ctx context.Context) *serviceconfig.ParseResult {\n\tss, err := d.resolver.LookupTXT(ctx, txtPrefix+d.host)\n\tif err != nil {\n\t\tif envconfig.TXTErrIgnore {\n\t\t\treturn nil\n\t\t}\n\t\tif err = handleDNSError(err, \"TXT\"); err != nil {\n\t\t\treturn &serviceconfig.ParseResult{Err: err}\n\t\t}\n\t\treturn nil\n\t}\n\tvar res string\n\tfor _, s := range ss {","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/resolver/dns/dns_resolver.go#L269-L305","documentation":"handleDNSError (dns_resolver.go:278-291) inspects errors from LookupSRV/LookupTXT/LookupHost. Transient or timeout DNS errors are wrapped with this message and logged at Info level so gRPC can back off and retry; non-timeout, non-temporary failures (e.g. NXDOMAIN) are suppressed (returned as nil). The %v fields are the lookup type (SRV/TXT/A) and the underlying net.DNSError.","triggerScenarios":"Triggered when a DNS lookup returns a *net.DNSError whose IsTimeout or IsTemporary is true, or when a non-DNSError is returned. The lookup type label ('A', 'SRV', 'TXT') indicates which resolution failed.","commonSituations":"DNS server unreachable, packet loss to the resolver, very slow DNS causing timeouts, a temporary network partition, /etc/resolv.conf pointing at an unreachable nameserver, or container DNS misconfiguration.","solutions":["Verify the DNS server configured in /etc/resolv.conf (or container dnsPolicy) is reachable: nslookup <host> <server>.","Increase the gRPC DNS resolution timeout (ResolvingTimeout) if the resolver is legitimately slow.","Add retries / circuit-breaking at the caller; gRPC itself will back off, but upstream code should tolerate transient resolution failures.","If running in Kubernetes, confirm dnsPolicy (ClusterFirst) and that the cluster-dns Service is healthy."],"exampleFix":"// before: default resolution timeout too short for slow DNS\n\n// after:\n//   import (\n//       dns \"google.golang.org/grpc/internal/resolver/dns\"\n//   )\n//   // (configure via available options / environment as supported by your gRPC version)\n//   // and ensure resolv.conf points to a reachable nameserver","handlingStrategy":"retry","validationCode":"package main\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n)\n\n// Probe DNS reachability at startup so transient resolver problems surface\n// before traffic flows.\nfunc probeDNS(ctx context.Context, host string) error {\n\tctx, cancel := context.WithTimeout(ctx, 2*time.Second)\n\tdefer cancel()\n\tvar r net.Resolver\n\t_, err := r.LookupHost(ctx, host)\n\treturn err\n}\n\n// func main() { _ = probeDNS }","typeGuard":null,"tryCatchPattern":"// Transient DNS errors cause gRPC connection TRANSIENT_FAILURE with backoff;\n// it will retry automatically. Surface it via connection-state watching.\n//\n//   for {\n//       conn.WaitForStateChange(ctx, conn.GetState())\n//       if conn.GetState() == connectivity.Ready { break }\n//   }","preventionTips":["Point /etc/resolv.conf at a reliable, low-latency resolver.","In Kubernetes, verify kube-dns/CoreDNS health and dnsPolicy.","Tune ResolvingTimeout upward if your DNS is legitimately slow.","Monitor DNS error rates and alert on sustained transient failures."],"tags":["dns","resolver","network","timeout","retry","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}