JuliusBrussee/caveman · error
ssrf: could not parse resolved IP %v for host %q
Error message
ssrf: could not parse resolved IP %v for host %q
What it means
Defensive parse failure in ssrf.ValidateURL: the resolver returned an IP that netip.AddrFromSlice could not convert. In practice this is near-impossible (LookupIPAddr yields 4/16-byte slices), so seeing it indicates resolver corruption or a pathological environment rather than a user mistake.
Source
Thrown at shared/platform/ssrf/ssrf.go:229
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.
func validateHostInput(host string) error {
if host == "" {
return errors.New("ssrf: invalid host")
}
if _, err := netip.ParseAddr(host); err == nil {
return nilView on GitHub (pinned to 27d5a3981a)
Solutions
- Check what the resolver actually returns for the host (`dig example.com A`, `dig example.com AAAA`).
- Point the process at a trustworthy recursive resolver (system/default) instead of the odd one in front of it.
- If this appears in tests, fix the DNS stub to return valid net.IP values.
Defensive patterns
Strategy: try-catch
Try / catch
if err := ssrf.ValidateURL(ctx, raw, cfg); err != nil {
if strings.Contains(err.Error(), "could not parse resolved IP") {
// environment/resolver fault, not user input: alert ops
}
} Prevention
- Use the system default resolver; avoid hand-rolled DNS responders in the request path.
- In tests, stub resolvers with valid 4/16-byte IPs only.
When it happens
Trigger: A DNS response handing back a malformed address that still parses into a non-4/non-16 byte slice; essentially only reachable with a broken or hostile resolver in front of the process.
Common situations: Custom/captive DNS servers returning non-standard records; exotic test doubles stubbing LookupIPAddr with invalid data. Production users effectively never see this.
Related errors
- ssrf: DNS resolution failed for %q: %w
- ssrf: host %q resolved to no addresses
- 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/635c78eb5a94eb19.
Report an issue: GitHub.