Billionmail/BillionMail · error
resolve failed
Error message
resolve failed
What it means
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.
Source
Thrown at core/internal/service/domains/blacklist.go:355
m.SetQuestion(dns.Fqdn(domain), dns.TypeA)
var server string
if len(servers) > 0 {
server = servers[0] + ":53"
} else {
server = "8.8.8.8:53" // fallback
}
for i := 0; i < 2; i++ {
r, _, err := c.Exchange(m, server)
if err == nil && len(r.Answer) > 0 {
if a, ok := r.Answer[0].(*dns.A); ok {
return a.A.String(), nil
}
}
time.Sleep(time.Second)
}
return "", fmt.Errorf("resolve failed")
}
// addBlacklist
func addBlacklist(domain string, blcheck_info *model.BlacklistCheckResult) {
path := public.AbsPath("../core/data/blcheck_count.json")
data := make(map[string]*model.BlacklistCheckResult)
if gfile.Exists(path) {
content := gfile.GetContents(path)
_ = json.Unmarshal([]byte(content), &data)
}
data[domain] = blcheck_info
json_data, _ := json.Marshal(data)
_ = gfile.PutContents(path, string(json_data))
}
// ReverseIP
func ReverseIP(ip string) string {View on GitHub (pinned to fc36c76c05)
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
Example fix
// before
return "", fmt.Errorf("resolve failed")
// after
return "", fmt.Errorf("resolve failed for %s after %d attempts: last err %v", domain, retries, lastErr) Defensive patterns
Strategy: retry
Validate before calling
// pre-check resolvability before blacklist scanning
ips, err := net.LookupIP(domain)
if err != nil || len(ips) == 0 {
return fmt.Errorf("domain %s not resolvable, skip blacklist check", domain)
} Try / catch
ip, err := ResolveA(domain)
if err != nil {
if err.Error() == "resolve failed" {
// treat as transient/unresolvable: log and continue with other domains
continue
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to get server IP: %v
- cartesia clone API call: %w
- base URL not configured
- failed to list containers: %w
- No SPF record found (from cache)
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/7abca0bebdf01798.
Report an issue: GitHub.