JuliusBrussee/caveman · error
ssrf: host %q resolved to no addresses
Error message
ssrf: host %q resolved to no addresses
What it means
Pre-flight check in ssrf.ValidateURL: the hostname resolved without error but LookupIPAddr returned zero addresses. Treated as a hard failure because there is nothing to range-check — an empty answer must not be allowed to slip past the SSRF policy.
Source
Thrown at shared/platform/ssrf/ssrf.go:223
}
// "localhost" is explicitly blocked regardless of what DNS says — unless a
// self-hosted operator allowlisted it (resolution still runs, so every
// resolved address is range-checked below like any other).
if strings.EqualFold(host, "localhost") && !(!cfg.ManagedMode && isInAllowList(host, port, cfg.AllowList)) {
return fmt.Errorf("ssrf: host %q is blocked (loopback)", host)
}
// Resolve ALL addresses the hostname currently maps to. A hostname that
// returns even one blocked address is rejected (defense-in-depth against
// split-horizon / DNS rebinding scenarios where the pre-flight check and
// the dial see different answers).
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return fmt.Errorf("ssrf: DNS resolution failed for %q: %w", host, err)
}
if len(addrs) == 0 {
return fmt.Errorf("ssrf: host %q resolved to no addresses", host)
}
for _, ia := range addrs {
a, ok := netip.AddrFromSlice(ia.IP)
if !ok {
return fmt.Errorf("ssrf: could not parse resolved IP %v for host %q", ia.IP, host)
}
a = a.Unmap() // normalise ::ffff:x.x.x.x → x.x.x.x
if err := checkAddr(a, host, port, cfg); err != nil {
return err
}
}
return nil
}
// validateHostInput rejects URL/userinfo-shaped values before they reach DNS
// or an error formatter. IP literals (including zoned IPv6) are handled by
// netip.ParseAddr and may contain colons or a zone identifier.View on GitHub (pinned to 27d5a3981a)
Solutions
- Verify the name actually has A/AAAA records: `dig example.com A +short` should list addresses.
- Fix the DNS record or use the correct service hostname.
- If using a search-domain/suffix setup, use the FQDN with trailing dot if the bare short name resolves empty.
Defensive patterns
Strategy: validation
Validate before calling
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, u.Hostname())
if err == nil && len(addrs) == 0 {
return fmt.Errorf("hostname has no address records")
} Prevention
- Check that integration hostnames have A/AAAA records, not just a DNS zone.
- Beware MX-only domains (email providers) being reused as API hosts.
When it happens
Trigger: Calling ssrf.ValidateURL for a name whose DNS answer contains no A/AAAA records (e.g. only MX/NS records exist, or a stale empty RRset), or a resolver returning an empty NOERROR answer.
Common situations: Zone exists but the A record was deleted; hostname taken from an email domain (MX-only); CDN/registrar placeholder zones with no address records.
Related errors
- ssrf: DNS resolution failed for %q: %w
- ssrf: could not parse resolved IP %v for host %q
- ssrf: scheme %q not permitted (managed mode requires https)
- ssrf: host %q is blocked (loopback)
- ssrf: destination %s (for host %q) is in blocked range %s
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/05e41e8b00d2e231.
Report an issue: GitHub.