crewAIInc/crewAI · error · ValueError

URL scheme '{parsed.scheme}' is not allowed. Only http and h

Error message

URL scheme '{parsed.scheme}' is not allowed. Only http and https are supported.

What it means

Raised by the same URL validator for any scheme that is not http or https (file:// gets its own dedicated error first). ftp://, ws://, gopher://, javascript:, data:, or scheme-less strings that urlparse parses with an empty scheme all fail here. The validator's purpose is SSRF reduction: only web schemes proceed to DNS resolution and private-IP blocking.

Source

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

        logger.warning(
            "%s is enabled — skipping URL validation for: %s",
            _UNSAFE_PATHS_ENV,
            url,
        )
        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}. "

View on GitHub (pinned to 754d7323be)

Solutions

  1. Use full http:// or https:// URLs including the scheme.
  2. Normalize bare hostnames before validation: if '://' not in url: url = 'https://' + url.
  3. Route non-web resources to the right loader: ftp/file resources to a path-based loader after downloading, not the URL validator.
  4. Validate scheme on your own input boundary so your error messages are domain-specific.

Example fix

# before
validate_url("example.com/docs")     # empty scheme -> ValueError
validate_url("ftp://example.com/f")    # ftp -> ValueError

# after
def normalize(url: str) -> str:
    return url if "://" in url else f"https://{url}"

validate_url(normalize("example.com/docs"))  # ok
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def normalize_web_url(url: str) -> str | None:
    if "://" not in url:
        url = "https://" + url
    return url if urlparse(url).scheme in ("http", "https") else None

Try / catch

try:
    validated = validate_url(candidate)
except ValueError as e:
    if "URL scheme" in str(e):
        candidate = normalize_web_url(candidate)
        validated = validate_url(candidate) if candidate else reject(candidate)
    else:
        raise

Prevention

When it happens

Trigger: Passing ftp://example.com/file, ws://host/socket, or data:text/html,... to a URL-accepting loader/tool; passing a bare hostname like 'example.com/path' (urlparse yields scheme ''); URIs from config or LLM output using non-web schemes.

Common situations: Config values meant to be web URLs accidentally holding other protocol DSNs; mixed input lists where some entries are file paths or e-mail-style URIs (mailto:); penetration tests probing scheme handling; users pasting FTP download links.

Related errors


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