{"record":{"id":"a4c19758d39844a2","repo":"thanos-io/thanos","slug":"could-not-resolve-s-no-servers-returned-a-viable","errorCode":null,"errorMessage":"could not resolve %s: no servers returned a viable answer. Errs %v","messagePattern":"could not resolve (.+?): no servers returned a viable answer\\. Errs (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/discovery/dns/miekgdns/lookup.go","lineNumber":114,"sourceCode":"\tclient := &dns.Client{}\n\n\tvar errs []error\n\n\t// TODO(bwplotka): Worth to do fanout and grab fastest as golang native lib?\n\tfor _, server := range conf.Servers {\n\t\tservAddr := net.JoinHostPort(server, conf.Port)\n\t\tmsg, err := askServerForName(name, qtype, client, servAddr, true)\n\t\tif err != nil {\n\t\t\terrs = append(errs, errors.Wrapf(err, \"resolution against server %s for %s\", server, name))\n\t\t\tcontinue\n\t\t}\n\n\t\tif msg.Rcode == dns.RcodeSuccess || msg.Rcode == dns.RcodeNameError {\n\t\t\treturn msg, nil\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(\"could not resolve %s: no servers returned a viable answer. Errs %v\", name, fmtErrs(errs))\n}\n\nfunc fmtErrs(errs []error) string {\n\tb := bytes.Buffer{}\n\tfor _, err := range errs {\n\t\tb.WriteString(\";\")\n\t\tb.WriteString(err.Error())\n\t}\n\treturn b.String()\n}\n\n// askServerForName makes a request to a specific DNS server for a specific\n// name (and qtype). Retries with TCP in the event of response truncation,\n// but otherwise just sends back whatever the server gave, whether that be a\n// valid-looking response, or an error.\nfunc askServerForName(name string, qType dns.Type, client *dns.Client, servAddr string, edns bool) (*dns.Msg, error) {\n\tmsg := &dns.Msg{}\n","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/discovery/dns/miekgdns/lookup.go#L96-L132","documentation":"Returned when lookupFromAnyServer exhausted every configured DNS server without a viable answer: each server either errored (collected in errs) or returned a non-success/non-NXDOMAIN rcode such as SERVFAIL. lookupWithSearchPath then fails with this aggregate for the specific name being resolved.","triggerScenarios":"All servers in conf.Servers fail to answer or return rcodes other than RcodeSuccess/RcodeNameError for the requested name and qtype (A/AAAA/SRV).","commonSituations":"Upstream DNS SERVFAIL due to broken zone delegation; recursive resolver unable to reach authoritative servers; rate limiting or response filtering by corporate DNS; DNSSEC validation failures.","solutions":["Check the 'Errs' list at the end of the message for the per-server failure reasons","Run dig +trace <name> to see where in the delegation chain resolution breaks","Retry after confirming the authoritative zone for the name is healthy","Switch to a known-good resolver (e.g. fix resolv.conf nameserver entries)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify the name resolves via a reference resolver first\nif err := probeDNS(\"8.8.8.8\", name); err != nil { /* name itself is broken upstream */ }","typeGuard":null,"tryCatchPattern":"addrs, err := r.LookupIPAddr(ctx, name)\nif err != nil && strings.Contains(err.Error(), \"no servers returned a viable answer\") {\n    // SERVFAIL-style transient: back off and retry\n    time.Sleep(backoff); addrs, err = r.LookupIPAddr(ctx, name)\n}","preventionTips":["Monitor upstream/authoritative DNS health for zones you depend on","Use multiple independent resolvers in resolv.conf for redundancy","Check dig +trace when persistent SERVFAILs appear to locate the broken delegation","Cache last-good discovery results to ride out transient SERVFAILs"],"tags":["dns","servfail","resolution-failed"],"backgroundTag":"dns-resolution-failed","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}