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
- Normalize URLs to include an allowed scheme (`https://`) — check for a missing scheme prefix when the error shows `scheme ''`.
- If a non-default scheme is genuinely required, construct the policy with `allowed_schemes=frozenset({'https', 'ftp'})` — but only for trusted internal use.
- 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
- Normalize user-supplied URLs (default scheme, strip whitespace) before validation.
- Validate scheme at the API boundary so bad URLs are rejected with a 4xx, not a fetch-time error.
- Never widen allowed_schemes for untrusted input; file:// and ftp:// are blocked for good reason.
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.