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

  1. 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).
  2. 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.
  3. 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

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


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/23610d083e8b2f49. Report an issue: GitHub.