BerriAI/litellm · error · SSRFError
Too many redirects
Error message
Too many redirects
What it means
Raised by safe_get (sync) after the redirect loop exceeds _MAX_REDIRECTS (10) hops. Each hop is validated for SSRF, and if every response keeps redirecting, the loop terminates with this SSRFError instead of following forever. It signals either a genuine redirect loop (A -> B -> A) or a redirect chain longer than 10 hops on the target server.
Source
Thrown at litellm/litellm_core_utils/url_utils.py:412
if not getattr(litellm, "user_url_validation", True):
kwargs.setdefault("follow_redirects", True)
return client.get(url, **kwargs)
kwargs.pop("follow_redirects", None)
caller_headers: Final = kwargs.pop("headers", {})
for _ in range(_MAX_REDIRECTS):
validated_url, original_host = validate_url(url)
response = client.get(
validated_url,
headers={**caller_headers, "Host": original_host},
follow_redirects=False,
**kwargs,
)
if not response.is_redirect:
return response
# Resolve the next hop against the ORIGINAL (pre-rewrite) URL so
# relative Location headers keep the original hostname.
url = _extract_redirect_url(response, url)
raise SSRFError("Too many redirects")
async def async_safe_get(client: Any, url: str, **kwargs: Any) -> Any:
"""Async version of safe_get."""
if not getattr(litellm, "user_url_validation", True):
kwargs.setdefault("follow_redirects", True)
return await client.get(url, **kwargs)
kwargs.pop("follow_redirects", None)
caller_headers: Final = kwargs.pop("headers", {})
for _ in range(_MAX_REDIRECTS):
validated_url, original_host = validate_url(url)
response = await client.get(
validated_url,
headers={**caller_headers, "Host": original_host},
follow_redirects=False,
**kwargs,
)
if not response.is_redirect:View on GitHub (pinned to 6c2dcb801b)
Solutions
- Trace the chain manually (curl -IL or curl with --max-redirs) to find where it loops and fix the server-side redirect logic.
- Request the final destination URL directly, bypassing the chain.
- Fix common loop causes: consistent scheme (always https), consistent trailing slash, consistent host (www vs apex).
- Note the cap is hardcoded at 10 in litellm — chains longer than that cannot be followed via safe_get.
Example fix
# before: loop between http and https on server resp = safe_get(client, "http://api.example.com/f") # after: use the canonical scheme/URL directly resp = safe_get(client, "https://api.example.com/f")
Defensive patterns
Strategy: try-catch
Validate before calling
import httpx
def resolves_within_limit(url: str, limit: int = 10) -> bool:
with httpx.Client(follow_redirects=False) as c:
current, hops = url, 0
while hops < limit:
r = c.head(current, timeout=10)
if not r.is_redirect:
return True
current = str(httpx.URL(current).join(r.headers["location"]))
hops += 1
return False Try / catch
from litellm.litellm_core_utils.url_utils import SSRFError
try:
resp = safe_get(client, url)
except SSRFError as e:
if "Too many redirects" in str(e):
return bad_gateway("redirect loop at upstream")
raise Prevention
- Pre-resolve chains with curl -IL to detect loops before wiring a URL into litellm.
- Enforce consistent scheme/slash/host on your servers to avoid redirect ping-pong.
- Do not retry on this error — a loop will loop again; fix the upstream.
When it happens
Trigger: safe_get on a URL participating in a redirect cycle (e.g. http -> https -> http on the same path, or two endpoints redirecting to each other), or a legitimately long chain (11+ hops) of same-origin redirects — each hop must also pass origin checks, so loops among a handful of URLs are the usual cause.
Common situations: Misconfigured web servers forcing scheme/port toggles that loop; login pages redirecting in a circle when unauthenticated; trailing-slash redirect loops (path with and without slash each redirecting to the other); OAuth flows longer than 10 hops.
Related errors
- Redirect response has no Location header
- URL scheme is not allowed
- Origin mismatch on scheme
- Origin mismatch on host
- Origin mismatch on port
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/51f6552f86d903a0.
Report an issue: GitHub.