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
- Use full http:// or https:// URLs including the scheme.
- Normalize bare hostnames before validation: if '://' not in url: url = 'https://' + url.
- Route non-web resources to the right loader: ftp/file resources to a path-based loader after downloading, not the URL validator.
- 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
- Always emit fully-qualified https:// URLs from your own code and config.
- Auto-prepend a scheme to bare hostnames before validation.
- Reject non-web schemes early with a clear domain-specific error message.
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
- URL has no hostname: '{url}'
- file:// URLs are not allowed: '{url}'. Use a file path inste
- URL '{url}' resolves to private/reserved IP {ip_str}. Access
- Blocked unsafe {label}: {e}
- Blocked unsafe URL: {e}
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/24424933f155630b.
Report an issue: GitHub.