{"record":{"id":"7abca0bebdf01798","repo":"Billionmail/BillionMail","slug":"resolve-failed","errorCode":null,"errorMessage":"resolve failed","messagePattern":"resolve failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/domains/blacklist.go","lineNumber":355,"sourceCode":"\tm.SetQuestion(dns.Fqdn(domain), dns.TypeA)\n\n\tvar server string\n\tif len(servers) > 0 {\n\t\tserver = servers[0] + \":53\"\n\t} else {\n\t\tserver = \"8.8.8.8:53\" // fallback\n\t}\n\n\tfor i := 0; i < 2; i++ {\n\t\tr, _, err := c.Exchange(m, server)\n\t\tif err == nil && len(r.Answer) > 0 {\n\t\t\tif a, ok := r.Answer[0].(*dns.A); ok {\n\t\t\t\treturn a.A.String(), nil\n\t\t\t}\n\t\t}\n\t\ttime.Sleep(time.Second)\n\t}\n\treturn \"\", fmt.Errorf(\"resolve failed\")\n}\n\n// addBlacklist\nfunc addBlacklist(domain string, blcheck_info *model.BlacklistCheckResult) {\n\n\tpath := public.AbsPath(\"../core/data/blcheck_count.json\")\n\tdata := make(map[string]*model.BlacklistCheckResult)\n\tif gfile.Exists(path) {\n\t\tcontent := gfile.GetContents(path)\n\t\t_ = json.Unmarshal([]byte(content), &data)\n\t}\n\tdata[domain] = blcheck_info\n\tjson_data, _ := json.Marshal(data)\n\t_ = gfile.PutContents(path, string(json_data))\n}\n\n// ReverseIP\nfunc ReverseIP(ip string) string {","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/domains/blacklist.go#L337-L373","documentation":"ResolveA in domains/blacklist.go performs a DNS A-record lookup with retries (sleeping 1 second between attempts) and, if no *dns.A answer is obtained after all attempts, returns the sentinel error 'resolve failed'. It is a plain fmt.Errorf with no wrapped cause, so the actual DNS failure reason (timeout, NXDOMAIN, no A record) is discarded.","triggerScenarios":"CheckBlacklist, CheckDomainsBlacklist, or IsDomainBlacklist calls ResolveA for an IP/host whose DNS query repeatedly returns no A record answer (r.Answer empty, or first answer not *dns.A) until the retry loop is exhausted.","commonSituations":"Querying a hostname that only has AAAA records; DNS server down or unreachable from the container/host; querying an IP literal in contexts expecting a name; corporate firewalls blocking UDP/TCP 53; transient resolver failures during blacklist scans of many domains.","solutions":["Verify the domain actually has an A record: dig A <domain> using the same resolver the server uses","Check resolver configuration (/etc/resolv.conf) and that outbound port 53 is open","Add the underlying dns error to the returned error instead of the bare sentinel for diagnosability","Increase retries or use net.Resolver with context and timeouts instead of fixed 1-second sleeps"],"exampleFix":"// before\nreturn \"\", fmt.Errorf(\"resolve failed\")\n// after\nreturn \"\", fmt.Errorf(\"resolve failed for %s after %d attempts: last err %v\", domain, retries, lastErr)","handlingStrategy":"retry","validationCode":"// pre-check resolvability before blacklist scanning\nips, err := net.LookupIP(domain)\nif err != nil || len(ips) == 0 {\n\treturn fmt.Errorf(\"domain %s not resolvable, skip blacklist check\", domain)\n}","typeGuard":null,"tryCatchPattern":"ip, err := ResolveA(domain)\nif err != nil {\n\tif err.Error() == \"resolve failed\" {\n\t\t// treat as transient/unresolvable: log and continue with other domains\n\t\tcontinue\n\t}\n\treturn err\n}","preventionTips":["Confirm outbound DNS (UDP/TCP 53) is allowed from the host/container","Skip or flag domains without A records before running blacklist checks","Use a context-aware resolver with explicit timeouts rather than fixed sleeps","Return the underlying resolver error for diagnosability"],"tags":["dns","network","blacklist"],"backgroundTag":"dns-resolution-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}