langchain-ai/langchain · error · SSRFBlockedError

scheme '{scheme}' not allowed

Error message

scheme '{scheme}' not allowed

What it means

Raised by `validate_url_sync` (and the shared scheme check) when the URL's scheme, lowercased, is not in `policy.allowed_schemes` — the default allows only http/https. This is the first guard in the chain: schemes like `file://`, `ftp://`, `gopher://`, or javascript/data URIs are rejected before hostname or DNS checks run, since non-HTTP fetchers routinely bypass IP-level protections.

Source

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

    for _family, _type, _proto, _canonname, sockaddr in addrinfo:
        validate_resolved_ip(str(sockaddr[0]), policy)


def validate_url_sync(url: str, policy: SSRFPolicy = DEFAULT_SSRF_POLICY) -> None:
    """Synchronous URL validation (no DNS resolution).

    Suitable for Pydantic validators and other sync contexts. Checks scheme
    and hostname patterns only - use `validate_url` for full DNS-aware checking.

    Raises:
        SSRFBlockedError: If the URL violates the policy.
    """
    parsed = urllib.parse.urlparse(url)

    scheme = (parsed.scheme or "").lower()
    if scheme not in policy.allowed_schemes:
        msg = f"scheme '{scheme}' not allowed"
        raise SSRFBlockedError(msg)

    hostname = parsed.hostname
    if not hostname:
        msg = "missing hostname"
        raise SSRFBlockedError(msg)

    allowed = _effective_allowed_hosts(policy)
    if hostname.lower() in {h.lower() for h in allowed}:
        return

    try:
        ipaddress.ip_address(hostname)
        validate_resolved_ip(hostname, policy)
    except SSRFBlockedError:
        raise
    except ValueError:
        pass
    else:

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Normalize URLs to include an allowed scheme (`https://`) — check for a missing scheme prefix when the error shows `scheme ''`.
  2. If a non-default scheme is genuinely required, construct the policy with `allowed_schemes=frozenset({'https', 'ftp'})` — but only for trusted internal use.
  3. Reject/repair at input time: validate user URLs before they reach the fetch layer.

Example fix

# before
validate_url_sync('example.com/api', policy)  # scheme '' not allowed

# after
from urllib.parse import urlunparse
url = url if '://' in url else f'https://{url}'
validate_url_sync(url, policy)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def has_allowed_scheme(url: str, allowed={"http", "https"}) -> bool:
    return (urlparse(url).scheme or "").lower() in allowed

def normalize_url(url: str) -> str:
    return url if "://" in url else f"https://{url}"

Try / catch

from langchain_core._security._policy import SSRFBlockedError

try:
    validate_url_sync(url, policy)
except SSRFBlockedError as e:
    if str(e).startswith("scheme"):
        raise InvalidUserInput(url) from e  # input problem, not policy — no retry
    raise

Prevention

When it happens

Trigger: `validate_url_sync('file:///etc/passwd')`, `validate_url_sync('ftp://host/file')`, or an empty/relative URL like `'/api/thing'` where `parsed.scheme` is `''` (empty scheme fails the membership test and renders as "scheme '' not allowed"). Also triggered when a custom policy restricts to `frozenset({'https'})` and an http:// URL is passed.

Common situations: User- or model-supplied URLs in fetch tools that smuggle `file://` reads (the exact attack the guard exists for); misconfigured base URLs missing the scheme; and legitimate-but-blocked schemes when someone points a loader at an internal `ftp://` or custom-scheme endpoint.

Related errors


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