crewAIInc/crewAI · error · ValueError
URL '{url}' resolves to private/reserved IP {ip_str}. Access
Error message
URL '{url}' resolves to private/reserved IP {ip_str}. Access to internal networks is not allowed. Set {_UNSAFE_PATHS_ENV}=true to bypass. What it means
The core SSRF protection in crewai_tools: after resolving a URL's hostname, every returned address is checked against private/reserved ranges (loopback, RFC1918, link-local, etc.). If any resolved IP is private or reserved, the request is refused with this ValueError. This prevents agents from being tricked into hitting internal services (e.g. cloud metadata at 169.254.169.254).
Source
Thrown at lib/crewai-tools/src/crewai_tools/security/safe_path.py:236
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
- If the target is a legitimate internal/local service you control, set the documented bypass env var (e.g. CREWAI_TOOLS_UNSAFE_PATHS/true per _UNSAFE_PATHS_ENV) only in that trusted environment.
- Point the tool at the public address of the service instead of the private one.
- If this appears while processing untrusted web content, treat it as the guard working as intended — do not bypass; audit where the URL came from.
- For local testing, prefer passing a file path (for file-backed tools) over a localhost URL.
Example fix
# before (guard blocks internal target)
tool.run("http://localhost:8000/report")
# after (explicit opt-in for trusted local testing only)
# export CREWAI_UNSAFE_PATHS=true (name per _UNSAFE_PATHS_ENV in safe_path.py)
tool.run("http://localhost:8000/report") Defensive patterns
Strategy: try-catch
Validate before calling
import ipaddress, socket
from urllib.parse import urlparse
def resolves_to_public_only(url: str) -> bool:
host = urlparse(url).hostname
if not host:
return False
try:
infos = socket.getaddrinfo(host, None)
except socket.gaierror:
return False
ips = {ipaddress.ip_address(i[4][0]) for i in infos}
return all(ip.is_global for ip in ips) Try / catch
try:
fetch_url_body(url, max_bytes=1_000_000)
except ValueError as e:
if "private/reserved IP" in str(e):
# treat as untrusted content; log and skip rather than bypass
logger.warning("Blocked internal-network URL: %s", url)
return None
raise Prevention
- Treat this error as a security signal, not an obstacle — never blanket-disable the guard in production.
- Pre-filter agent-supplied URLs through your own is_global IP check before invoking fetch tools.
- If local testing needs a bypass, scope the env var to the test process only and never ship it.
When it happens
Trigger: A URL pointing at 'http://localhost:8080/', 'http://192.168.1.10/api', 'http://169.254.169.254/latest/meta-data/', or a public-looking hostname whose DNS has records that resolve to internal IPs (DNS rebinding or split-horizon DNS).
Common situations: LLM-driven fetch tools following links embedded in scraped content that target internal addresses; developers testing against local dev servers (localhost:8000) through a guarded tool; corporate split-horizon DNS where an internal name resolves differently inside the network.
Related errors
- file:// URLs are not allowed: '{url}'. Use a file path inste
- URL scheme '{parsed.scheme}' is not allowed. Only http and h
- URL has no hostname: '{url}'
- Could not resolve hostname: '{parsed.hostname}'
- Blocked unsafe {label}: {e}
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/31b8a0413a9d81fb.
Report an issue: GitHub.