Graphify-Labs/graphify · error · OSError
SSRF blocked: no usable address resolved from '{host}'
Error message
SSRF blocked: no usable address resolved from '{host}' What it means
Raised by _resolve_and_validate in graphify/security.py when getaddrinfo returned addresses but none of them were usable: every candidate was either blocked as private/reserved or was not a parseable IP (ValueError in ip_address, which is skipped). It is the 'no acceptable address left' counterpart to the single-IP block error, and like it is an OSError matching connect() failure semantics.
Source
Thrown at graphify/security.py:177
"""Resolve *host* once and return (family, validated_ip) for the first
address that is not in a blocked range.
Raises OSError if every resolved address is private/reserved/internal,
matching the failure mode urllib/http.client expect from connect().
"""
infos = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
for family, _type, _proto, _canon, sockaddr in infos:
addr = sockaddr[0]
try:
ip = ipaddress.ip_address(addr)
except ValueError:
continue
if _ip_is_blocked(ip):
raise OSError(
f"SSRF blocked: IP {addr} resolved from '{host}' is private/reserved"
)
return family, addr
raise OSError(f"SSRF blocked: no usable address resolved from '{host}'")
class _SSRFGuardedHTTPConnection(http.client.HTTPConnection):
"""HTTPConnection that resolves + validates DNS once, then connects to the
exact validated IP (no second resolution = no DNS-rebind TOCTOU)."""
def connect(self) -> None:
family, ip = _resolve_and_validate(self.host, self.port)
self.sock = socket.create_connection(
(ip, self.port),
self.timeout,
self.source_address,
)
if self._tunnel_host:
self._tunnel()
class _SSRFGuardedHTTPSConnection(http.client.HTTPSConnection):View on GitHub (pinned to 7fe58b0b0f)
Solutions
- Verify the host actually resolves to a public address: run socket.getaddrinfo(host, 443) and inspect the returned IPs.
- If the host is intentionally internal, move the fetch outside safe_fetch to a transport you explicitly control.
- Fix or bypass the DNS record / search domain that is mapping the name onto private addresses.
- If you need graphify to fetch from an allow-listed internal mirror, request an explicit allowlist feature upstream instead of monkey-patching _ip_is_blocked.
Example fix
# before
try:
data = safe_fetch("http://mirror.internal.example/pkg.json")
except OSError as e:
raise
# after: diagnose what the name resolves to, use a public mirror or local file
import socket
print(socket.getaddrinfo("mirror.internal.example", 80))
data = Path("pkg.json").read_bytes() Defensive patterns
Strategy: validation
Validate before calling
def has_usable_address(host: str) -> bool:
"""True if at least one resolved address is a public unicast IP."""
try:
infos = socket.getaddrinfo(host, 443, socket.AF_UNSPEC, socket.SOCK_STREAM)
except socket.gaierror:
return False
for *_rest, sockaddr in infos:
try:
ip = ipaddress.ip_address(sockaddr[0])
except ValueError:
continue
if not (ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved or ip.is_multicast):
return True
return False Type guard
def is_fetchable_host(host: str) -> bool:
return has_usable_address(host) Try / catch
try:
body = safe_fetch(url)
except OSError as e:
if str(e).startswith("SSRF blocked: no usable address"):
logger.warning("host %s has no public address; skipping", host)
return None
raise Prevention
- Check DNS from the same environment the fetch runs in (containers may resolve differently).
- Treat a name with only private records as a config bug, not a transient failure.
- Log the failing host once at config-load time to catch bad hostnames early.
When it happens
Trigger: Calling safe_fetch/safe_fetch_text where a host resolves only to blocked ranges (e.g. a name with only 127.0.0.1 and ::1 A/AAAA records), or where getaddrinfo returns sockaddrs whose address strings ip_address cannot parse, so every entry is skipped and the loop falls through.
Common situations: Hostnames that are internal-only aliases resolving exclusively to RFC1918 space; hostnames resolving to non-IP sockaddr entries on unusual platform resolvers; typo'd host that a search-domain wildcard resolves to an internal catch-all address.
Related errors
- Blocked private/internal IP {addr} (resolved from '{hostname
- SSRF blocked: IP {addr} resolved from '{host}' is private/re
- OLLAMA_BASE_URL points at a link-local/metadata address ({ho
- Blocked URL scheme '{parsed.scheme}' - only http and https a
- Blocked cloud metadata endpoint '{hostname}'. Got: {url!r}
AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14).
Data as JSON: /api/errors/41d263a34fed0aca.
Report an issue: GitHub.