oobabooga/textgen · error · ValueError
Access to non-public address {ip} is blocked
Error message
Access to non-public address {ip} is blocked What it means
Raised by _validate_url() during DNS resolution: the hostname resolves to at least one address that is not globally routable (not ip.is_global) — loopback, link-local, private ranges (10/8, 172.16/12, 192.168/16), CGNAT, multicast, etc. This is the core SSRF defense: it prevents the fetch layer from being used to reach internal services (cloud metadata at 169.254.169.254, internal admin panels, databases). Every address returned by getaddrinfo is checked, so a DNS name with mixed public/private A records still fails.
Source
Thrown at modules/web_search.py:38
raise ValueError("Invalid URL: backslashes are not allowed")
parsed = urlparse(url)
if parsed.scheme not in ('http', 'https'):
raise ValueError(f"Unsupported URL scheme: {parsed.scheme}")
if '@' in parsed.netloc:
raise ValueError("Invalid URL: userinfo (credentials) in URLs is not allowed")
hostname = parsed.hostname
if not hostname:
raise ValueError("No hostname in URL")
# Resolve hostname and check all returned addresses
try:
for family, _, _, _, sockaddr in socket.getaddrinfo(hostname, None):
ip = ipaddress.ip_address(sockaddr[0])
if not ip.is_global:
raise ValueError(f"Access to non-public address {ip} is blocked")
except socket.gaierror:
raise ValueError(f"Could not resolve hostname: {hostname}")
def safe_get(url, headers=None, timeout=10, max_redirects=5):
"""Fetch a URL with SSRF-safe redirect handling. Validates every hop."""
_validate_url(url)
for _ in range(max_redirects):
response = requests.get(url, headers=headers, timeout=timeout, allow_redirects=False)
if response.is_redirect and 'Location' in response.headers:
url = urljoin(url, response.headers['Location'])
_validate_url(url)
else:
return response
raise ValueError(f"Too many redirects (max {max_redirects})")
View on GitHub (pinned to ed888c71f2)
Solutions
- If you legitimately need an internal page, fetch it with a direct HTTP client outside this guarded API — the block is intentional.
- Check which IP the hostname resolves to (dig/getent) and use the public address or a public DNS name if split-horizon DNS is returning internal records.
- For maintainers: if internal fetching must be supported, add an explicit opt-in allowlist rather than weakening the is_global check.
- Treat hits on redirect hops as attacks or misconfiguration of the remote server; surface the error to the user instead of retrying.
Defensive patterns
Strategy: try-catch
Validate before calling
import ipaddress, socket
def resolves_to_public_host(url: str) -> bool:
host = urlparse(url).hostname
if not host:
return False
try:
return all(ipaddress.ip_address(sa[0]).is_global for *_r, sa in socket.getaddrinfo(host, None))
except socket.gaierror:
return False Try / catch
try:
resp = safe_get(url)
except ValueError as e:
if 'non-public address' in str(e):
log.warning('Blocked internal fetch attempt: %s', url) # intentional guard; do not bypass
resp = None
else:
raise Prevention
- Only submit public internet URLs to the web-fetch API; fetch internal resources with a direct client outside it.
- Do not attempt to work around the is_global check — it exists to block metadata-service and LAN attacks.
- In crawlers, catch and blacklist URLs that redirect to internal hosts.
When it happens
Trigger: Fetching a URL whose host resolves to a private IP: 'http://localhost/api', 'http://192.168.1.1/admin', 'http://169.254.169.254/latest/meta-data', or a public-looking domain whose DNS returns an internal address (DNS rebinding or split-horizon DNS). Also triggered on redirect hops whose Location points at an internal host.
Common situations: Attempting to fetch internal/intranet pages through the web-fetch feature; testing SSRF protections; a corporate split-horizon DNS where an internal name also resolves internally when queried from inside the network; redirect chains that bounce to an internal host.
Related errors
- Could not resolve hostname: {hostname}
- Invalid URL: backslashes are not allowed
- Unsupported URL scheme: {parsed.scheme}
- No hostname in URL
- Too many redirects (max {max_redirects})
AI-assisted analysis of oobabooga/textgen@ed888c71f2 (2026-08-15).
Data as JSON: /api/errors/641d0319132ba7ce.
Report an issue: GitHub.