{"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/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/resolver/dns/dns_resolver.go#L269-L305","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: default resolver times out\nconn, _ := grpc.Dial(\"dns:///svc:443\", ...)\n// after: pin a reliable authority\nconn, _ := grpc.Dial(\"dns:///8.8.8.8/svc:443\", ...)","handlingStrategy":"retry","validationCode":"// Health-check /etc/resolv.conf nameservers at startup.\nfunc dnsReachable() error {\n    r := &net.Resolver{PreferGo: true}\n    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n    defer cancel()\n    _, err := r.LookupHost(ctx, \"dns.google\")\n    return err\n}","typeGuard":null,"tryCatchPattern":"// The dns resolver already retries with exponential backoff.\n// Surface it as a metric; only act if it persists beyond a threshold.\nif status.Code(err) == codes.DeadlineExceeded {\n    metricDNSFailures.Inc()\n}","preventionTips":["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."],"tags":["grpc","resolver","dns","timeout","transient","network"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}