gofiber/fiber · error · ErrUpstreamHostBlocked
%w: %s has no usable address
Error message
%w: %s has no usable address
What it means
dialValidatedIPs walks the resolved IP list and skips IPv6 addresses when dialDualStack is false (mirroring fasthttp's IPv4-only default). If every candidate is skipped (IPv6-only host with dialDualStack=false) or the list was empty, this error is returned instead of a dial error. If dials were attempted but all failed, the last dial error is returned instead — so this specific message means no dial was even attempted.
Source
Thrown at middleware/proxy/security.go:492
// "no usable address" error is returned; otherwise the last dial error
// is propagated so the caller can see why each attempt failed.
//
//nolint:revive // dialDualStack mirrors fasthttp.HostClient.DialDualStack
func dialValidatedIPs(ips []net.IP, host, port string, dialDualStack bool, dial ssrfDialFunc) (net.Conn, error) {
var lastErr error
for _, ip := range ips {
// Mirror fasthttp's default of IPv4-only unless DialDualStack.
if !dialDualStack && ip.To4() == nil {
continue
}
conn, derr := dial("tcp", net.JoinHostPort(ip.String(), port))
if derr == nil {
return conn, nil
}
lastErr = derr
}
if lastErr == nil {
lastErr = fmt.Errorf("%w: %s has no usable address", ErrUpstreamHostBlocked, host)
}
return nil, lastErr
}
// installHostClientGuard fits hc with the policy-aware, dial-time SSRF
// guard. It is installed through fasthttp.Client.ConfigureClient, which
// runs once per HostClient at creation — so the guard is present before the
// first dial to that host and, crucially, covers BOTH dial code paths:
// fasthttp's callDialFunc prefers DialTimeout over Dial, so guarding only
// Dial would let a client that sets DialTimeout dial unvalidated. We wrap
// DialTimeout when present (preserving its per-request timeout) and always
// wrap Dial so the nil-DialTimeout and default-dialer paths are guarded too.
func installHostClientGuard(hc *fasthttp.HostClient) {
if hc.DialTimeout != nil {
hc.DialTimeout = newGuardedClientDialerWithTimeout(hc.DialTimeout, hc.DialDualStack)
}
hc.Dial = newGuardedClientDialer(hc.Dial, hc.DialDualStack)
}View on GitHub (pinned to a105acad6c)
Solutions
- Confirm the host has IPv4 (A) records if you cannot enable dual-stack: dig A hostname +short.
- If the upstream is legitimately IPv6-capable and your network supports it, enable DialDualStack on the HostClient / fasthttp.Client.
- If IPv4 is required, ask the upstream operator to publish A records.
- Verify your runtime network has IPv4 connectivity before relying on it.
- Log the resolved IPs at debug level to see what dialValidatedIPs received.
Example fix
// before: IPv6-only upstream, IPv4-default client balancer.HostClient.DialDualStack = false // 197 fires // after: enable dual stack balancer.HostClient.DialDualStack = true
Defensive patterns
Strategy: validation
Validate before calling
func hasIPv4(host string) bool {
ips, _ := net.LookupIP(host)
for _, ip := range ips { if ip.To4() != nil { return true } }
return false
} Prevention
- Confirm upstreams publish A records if DialDualStack is false.
- Enable DialDualStack when upstreams and your network are IPv6-capable.
- Log resolved IPs at debug level to see what dialValidatedIPs received.
- Audit HostClient.DialDualStack settings in config.
When it happens
Trigger: Hostname resolves to IPv6-only (AAAA-only, no A records) and the HostClient has DialDualStack=false (the fasthttp default); an empty resolved list slipped past earlier guards; all resolved IPs were skipped as IPv6.
Common situations: IPv6-only upstream service reached from an IPv4-default HostClient; a hostname that recently lost its A record; a service on an IPv6-only network (some mobile/telco backends); HostClient created without DialDualStack=true.
Related errors
- ErrUpstreamHostBlocked
- %w: %s lookup failed: %w
- proxy: invalid dial address %q: %w
- shutdown: graceful timeout has been reached, exiting
- remote address cannot be empty
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/06af572e6bebbf1c.
Report an issue: GitHub.