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

  1. Trace the chain manually (curl -IL or curl with --max-redirs) to find where it loops and fix the server-side redirect logic.
  2. Request the final destination URL directly, bypassing the chain.
  3. Fix common loop causes: consistent scheme (always https), consistent trailing slash, consistent host (www vs apex).
  4. 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

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


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/51f6552f86d903a0. Report an issue: GitHub.