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

  1. If you legitimately need an internal page, fetch it with a direct HTTP client outside this guarded API — the block is intentional.
  2. 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.
  3. For maintainers: if internal fetching must be supported, add an explicit opt-in allowlist rather than weakening the is_global check.
  4. 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

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


AI-assisted analysis of oobabooga/textgen@ed888c71f2 (2026-08-15). Data as JSON: /api/errors/641d0319132ba7ce. Report an issue: GitHub.