oobabooga/textgen · error · ValueError

No hostname in URL

Error message

No hostname in URL

What it means

Raised by _validate_url() when urlparse() returns an empty hostname. This happens for URLs that have a scheme but no authority component, such as 'file:///path', 'http:///foo', or URLs where the netloc was consumed by a preceding component the parser treats differently (e.g. after other validation failures leave a malformed URL). Without a hostname there is nothing to resolve or safety-check, so the request is refused.

Source

Thrown at modules/web_search.py:31


def _validate_url(url):
    """Validate that a URL is safe to fetch (not targeting private/internal networks)."""
    # Reject characters that cause parsing discrepancies between urlparse and requests,
    # which can be exploited to bypass SSRF protections (GHSA-27xf-58m5-vxmc).
    if '\\' in url:
        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'])

View on GitHub (pinned to ed888c71f2)

Solutions

  1. Fix the URL so it includes a real host: 'https://example.com/resource'.
  2. If a templated URL builder produced it, assert the host component is non-empty before calling the fetch API.
  3. Do not attempt to fetch local files through this API — read them with open()/Path directly instead.

Example fix

# before
resp = safe_get(f'https://{host}/page')  # host == '' -> 'https:///page' raises

# after
assert host, 'host must not be empty'
resp = safe_get(f'https://{host}/page')
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def url_has_host(url: str) -> bool:
    return bool(urlparse(url).hostname)

Try / catch

try:
    resp = safe_get(url)
except ValueError as e:
    if 'No hostname' in str(e):
        raise ValueError(f'Malformed URL (missing host): {url!r}') from e
    raise

Prevention

When it happens

Trigger: Calling safe_get()/download_web_page() with URLs like 'file:///etc/hosts', 'https:///resource', 'http://:8080/x' (empty host, port only), or scheme-only strings like 'https://'.

Common situations: User pastes a local file path with file:// expecting it to be fetched; malformed URLs from string concatenation where the host segment was dropped; scraping pipelines that build URLs from templated parts with an empty host variable.

Related errors


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