langchain-ai/langchain · error · ValueError
{exc}
Error message
{exc} What it means
This is the public `_ssrf_protection.py` helper (e.g. `check_url_ssrf` / url-for-fetch validation) re-raising an inner `SSRFBlockedError` from the synchronous scheme/hostname pre-check as a plain `ValueError(str(exc))`. The `{exc}` body is one of the SSRF reason strings — 'private IP range', 'localhost address', 'cloud metadata endpoint', 'Kubernetes internal DNS', 'scheme ... not allowed', or 'missing hostname' — so this error is the ValueError-facing envelope for all policy violations, chained via `from exc` to the original.
Source
Thrown at libs/core/langchain_core/_security/_ssrf_protection.py:82
url_str = str(url)
parsed = urlparse(url_str)
hostname = parsed.hostname or ""
# Test-environment bypass (preserved from original implementation)
if (
os.environ.get("LANGCHAIN_ENV") == "local_test"
and hostname.startswith("test")
and "server" in hostname
):
return url_str
policy = _policy_for(allow_private=allow_private, allow_http=allow_http)
# Synchronous scheme + hostname checks
try:
_validate_url_sync(url_str, policy)
except SSRFBlockedError as exc:
raise ValueError(str(exc)) from exc
# DNS resolution and IP validation
try:
addr_info = socket.getaddrinfo(
hostname,
parsed.port or (443 if parsed.scheme == "https" else 80),
socket.AF_UNSPEC,
socket.SOCK_STREAM,
)
for result in addr_info:
ip_str: str = result[4][0] # type: ignore[assignment]
try:
_validate_resolved_ip(ip_str, policy)
except SSRFBlockedError as exc:
raise ValueError(str(exc)) from exc
except socket.gaierror as e:View on GitHub (pinned to e32fa9a52e)
Solutions
- Read the message text — it names the exact policy reason; apply the matching fix (allow_private=True for trusted private endpoints, allowed_hosts for specific hostnames, correct scheme/host in the URL).
- For local dev, use `LANGCHAIN_ENV=local_test` with `test*server*` hostnames (the built-in bypass) or set `LANGCHAIN_ENV=local...` for the policy-level localhost allowlist.
- Keep `allow_private`/`allow_http` relaxation limited to explicitly trusted, user-configured base URLs — never for model-chosen URLs.
Example fix
# before
checked = check_url('http://192.168.0.5:9000/minio', allow_private=False)
# ValueError: private IP range
# after
checked = check_url('http://192.168.0.5:9000/minio', allow_private=True) Defensive patterns
Strategy: try-catch
Validate before calling
from urllib.parse import urlparse
import ipaddress, socket
def precheck_url(url: str) -> str | None:
"""Return the SSRF reason that would block this URL, or None."""
p = urlparse(url)
if (p.scheme or "").lower() not in {"http", "https"}:
return f"scheme '{p.scheme}' not allowed"
host = (p.hostname or "").lower()
if not host:
return "missing hostname"
try:
if ipaddress.ip_address(host).is_private:
return "private IP range"
except ValueError:
pass
return None # full policy check still required for DNS-resolved IPs Try / catch
try:
check_url(url, allow_private=host_is_trusted(url))
except ValueError as e:
msg = str(e)
if any(s in msg for s in ("private IP range", "localhost address", "cloud metadata", "Kubernetes")):
raise BlockedBySSRFPolicy(url, msg) from e # permanent: fix policy or URL
if "DNS" in msg:
retry_once() # transient resolution failure
raise Prevention
- Pass allow_private/allow_http explicitly from trusted configuration, never from untrusted input.
- For local dev, use LANGCHAIN_ENV=local_test with test*server* hostnames or LANGCHAIN_ENV=local for the localhost allowlist.
- Map the ValueError message back to the underlying SSRF reason before deciding retry vs fix — only DNS failures are transient.
When it happens
Trigger: Calling the public SSRF-check helper with a URL like `check_url('http://169.254.169.254/', ...)` or `check_url('http://localhost:8000/')` under default flags. The local-test escape hatch just above it returns early only when `LANGCHAIN_ENV == 'local_test'` AND the hostname starts with 'test' AND contains 'server' — any other host goes through full validation. Later in the same function DNS-resolved IPs are re-checked, so a hostname resolving to a blocked IP also lands here.
Common situations: Using a fetch utility built on this helper in local dev (localhost URLs), inside Kubernetes (`.svc` hostnames), or behind prompt-injected URLs in agent tools. Developers see a bare ValueError mentioning e.g. 'private IP range' and don't realize it comes from the SSRF layer or that `allow_private`/`allow_http` flags exist.
Related errors
- invalid IP address
- private IP range
- blocked CIDR
- localhost address
- Failed to resolve hostname '{hostname}': {e}
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/23610d083e8b2f49.
Report an issue: GitHub.