langchain-ai/langchain · error · SSRFBlockedError

private IP range

Error message

private IP range

What it means

`"private IP range"` is the reason string returned by `_ip_in_blocked_networks` and raised as `SSRFBlockedError` at the `raise SSRFBlockedError(reason)` line in `validate_resolved_ip` when the resolved IP falls inside a private/reserved network (RFC1918 10/8, 172.16/12, 192.168/16, link-local 169.254/16, IPv6 ULA fc00::/7, etc.) and the policy blocks private ranges (the default). This is the core SSRF defense: an attacker-controlled URL must not be able to reach internal network space.

Source

Thrown at libs/core/langchain_core/_security/_policy.py:212

def validate_resolved_ip(ip_str: str, policy: SSRFPolicy) -> None:
    """Validate a resolved IP address against the SSRF policy.

    Raises SSRFBlockedError if the IP is blocked.
    """
    try:
        addr = ipaddress.ip_address(ip_str)
    except ValueError as exc:
        msg = "invalid IP address"
        raise SSRFBlockedError(msg) from exc

    if isinstance(addr, ipaddress.IPv6Address):
        inner = _extract_embedded_ipv4(addr)
        if inner is not None:
            addr = inner

    reason = _ip_in_blocked_networks(addr, policy)
    if reason is not None:
        raise SSRFBlockedError(reason)


def validate_hostname(hostname: str, policy: SSRFPolicy) -> None:
    """Validate a hostname against the SSRF policy.

    Raises SSRFBlockedError if the hostname is blocked.
    """
    lower = hostname.lower()

    if policy.block_localhost and lower in _LOCALHOST_NAMES:
        msg = "localhost address"
        raise SSRFBlockedError(msg)

    if policy.block_cloud_metadata and lower in _CLOUD_METADATA_HOSTNAMES:
        msg = "cloud metadata endpoint"
        raise SSRFBlockedError(msg)

    if policy.block_k8s_internal and lower.endswith(_K8S_SUFFIX):

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. If the private endpoint is trusted, pass `allow_private=True` to the API that builds the policy (`_policy_for(allow_private=...)` — e.g. the `allow_private`/`allow_http` flags on the public SSRF helper in `_ssrf_protection.py`) so private ranges are permitted.
  2. Add the specific hostname to `policy.allowed_hosts` (and note `_effective_allowed_hosts` auto-allows `localhost`/`testserver` when `LANGCHAIN_ENV` starts with 'local').
  3. Point at a public endpoint or an allowed proxy instead of the internal address.
  4. For local dev/tests, set `LANGCHAIN_ENV=local` (or `local_test` for testserver-style hostnames) so the policy relaxes localhost/test hosts.

Example fix

# before
result = await validate_url('http://192.168.1.20:11434/api/generate', DEFAULT_SSRF_POLICY)
# SSRFBlockedError: private IP range

# after
policy = SSRFPolicy(allowed_hosts={'192.168.1.20'})
result = await validate_url('http://192.168.1.20:11434/api/generate', policy)
Defensive patterns

Strategy: try-catch

Validate before calling

import ipaddress

def is_private_target(host: str) -> bool:
    try:
        return ipaddress.ip_address(host).is_private
    except ValueError:
        return False  # hostname: resolution decides later

Try / catch

from langchain_core._security._policy import SSRFBlockedError

try:
    await validate_url(url, policy)
except SSRFBlockedError as e:
    if "private IP range" in str(e) and host_is_trusted_internal(url):
        return await validate_url(url, SSRFPolicy(allowed_hosts={urlparse(url).hostname}))
    raise

Prevention

When it happens

Trigger: `await validate_url('http://192.168.1.10:8080/api')`, `await validate_url('http://10.0.0.5/metadata')`, or a hostname like `my-internal-service.internal` that DNS resolves to a private IP, under the default `SSRFPolicy(block_private_ip=True)`. IPv4-mapped IPv6 addresses (`::ffff:10.0.0.5`) are unwrapped via `_extract_embedded_ipv4` and hit the same block.

Common situations: Running langchain locally or in a container where a legitimate internal endpoint (local Ollama at `http://192.168.x.x`, an internal gateway, a docker-network service) is the target; or in tests using `http://localhost`-adjacent private addresses. Developers are surprised because the URL works in curl but the library's SSRF guard blocks it before any request is made.

Related errors


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