crewAIInc/crewAI · error · ValueError

URL has no hostname: '{url}'

Error message

URL has no hostname: '{url}'

What it means

Thrown by validate_url() in crewai_tools' SSRF guard (safe_path.py) when a URL parses successfully but has no hostname component. The URL validator only permits http/https URLs that resolve to public IPs, and a missing hostname makes the DNS-based private-IP check impossible. This is a fail-fast security check, not a network error.

Source

Thrown at lib/crewai-tools/src/crewai_tools/security/safe_path.py:224

        return url

    parsed = urlparse(url)

    # Block file:// scheme
    if parsed.scheme == "file":
        raise ValueError(
            f"file:// URLs are not allowed: '{url}'. "
            f"Use a file path instead, or set {_UNSAFE_PATHS_ENV}=true to bypass."
        )

    # Only allow http and https
    if parsed.scheme not in ("http", "https"):
        raise ValueError(
            f"URL scheme '{parsed.scheme}' is not allowed. Only http and https are supported."
        )

    if not parsed.hostname:
        raise ValueError(f"URL has no hostname: '{url}'")

    try:
        addrinfos = socket.getaddrinfo(
            parsed.hostname, parsed.port or (443 if parsed.scheme == "https" else 80)
        )
    except socket.gaierror as exc:
        raise ValueError(f"Could not resolve hostname: '{parsed.hostname}'") from exc

    for _family, _, _, _, sockaddr in addrinfos:
        ip_str = str(sockaddr[0])
        if _is_private_or_reserved(ip_str):
            raise ValueError(
                f"URL '{url}' resolves to private/reserved IP {ip_str}. "
                f"Access to internal networks is not allowed. "
                f"Set {_UNSAFE_PATHS_ENV}=true to bypass."
            )

    return url

View on GitHub (pinned to 754d7323be)

Solutions

  1. Inspect the exact URL string being passed and fix the malformed host segment (ensure scheme is followed by a non-empty hostname, e.g. 'https://example.com/path').
  2. If the URL is built dynamically, log or assert the host component is non-empty before constructing the URL.
  3. If you genuinely need to load a local file, pass a file path instead of a file:// URL (the guard also rejects file:// separately).
  4. As a last-resort bypass in trusted environments only, set the documented unsafe-paths env var (referenced by _UNSAFE_PATHS_ENV) to 'true'.

Example fix

// before
url = f"http://{base_host}/data"  # base_host is '' -> 'http:///data'

# after
if not base_host:
    raise ValueError("base_host must be set")
url = f"http://{base_host}/data"
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

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

Try / catch

try:
    validate_url(url)
except ValueError as e:
    if "no hostname" in str(e):
        # fix the URL string, do not retry unchanged
        raise ValueError(f"Malformed URL, missing host: {url!r}") from e
    raise

Prevention

When it happens

Trigger: Calling a tool that fetches URLs (or validate_url directly) with strings like 'http:///path', 'https://:8443/x', 'http:///index.html', or a URL built by string concatenation where the host segment was empty or dropped (e.g. f"http://{host}/api" with host='').

Common situations: Building URLs from templates or config where the host variable is empty/None; stripping a hostname during URL sanitization; copy-paste typos with double slashes after the scheme; test fixtures using placeholder URLs without hosts.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/98ff0a5f8eaf15f4. Report an issue: GitHub.