{"id":"86a3b690b49e5a2a","repo":"psf/requests","slug":"exceeded-self-max-redirects-redirects","errorCode":null,"errorMessage":"Exceeded {self.max_redirects} redirects.","messagePattern":"Exceeded (.+?) redirects\\.","errorType":"exception","errorClass":"TooManyRedirects","httpStatus":null,"severity":"error","filePath":"src/requests/sessions.py","lineNumber":217,"sourceCode":"\n        hist: list[Response] = []  # keep track of history\n\n        url = self.get_redirect_target(resp)\n        previous_fragment = urlparse(req.url).fragment\n        while url:\n            prepared_request = req.copy()\n\n            # Update history and keep track of redirects.\n            resp.history = hist[:]\n            hist.append(resp)\n\n            try:\n                resp.content  # Consume socket so it can be released\n            except (ChunkedEncodingError, ContentDecodingError, RuntimeError):\n                resp.raw.read(decode_content=False)\n\n            if len(resp.history) >= self.max_redirects:\n                raise TooManyRedirects(\n                    f\"Exceeded {self.max_redirects} redirects.\", response=resp\n                )\n\n            # Release the connection back into the pool.\n            resp.close()\n\n            # Handle redirection without scheme (see: RFC 1808 Section 4)\n            if url.startswith(\"//\"):\n                parsed_rurl = urlparse(resp.url)\n                url = \":\".join([to_native_string(parsed_rurl.scheme), url])\n\n            # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)\n            parsed = urlparse(url)\n            if parsed.fragment == \"\" and previous_fragment:\n                parsed = parsed._replace(fragment=previous_fragment)\n            elif parsed.fragment:\n                previous_fragment = parsed.fragment\n            url = parsed.geturl()","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/psf/requests/blob/414f0513c33883adf6f2b46901d4f0b38a455851/src/requests/sessions.py#L199-L235","documentation":"Raised as `requests.exceptions.TooManyRedirects` from `Session.resolve_redirects()` in `src/requests/sessions.py:217` when the accumulated redirect history reaches `self.max_redirects` (default 30, from `DEFAULT_REDIRECT_LIMIT`). It is a loop guard: each hop is appended to `resp.history`, and once the limit is hit requests stops following and raises, attaching the last response as `e.response`.","triggerScenarios":"Any call with `allow_redirects=True` (the default for GET) where the server chain exceeds `max_redirects` hops — most often a redirect loop (A→B→A) or a login wall that redirects every request back to itself. Also triggered quickly if the user lowered `session.max_redirects`.","commonSituations":"Missing/expired auth cookies causing an endless bounce to a login page; http↔https or www↔apex rewrite loops in misconfigured nginx/CDN rules; sites that redirect based on a cookie or User-Agent the client never persists (using bare `requests.get` instead of a `Session` so cookies are dropped between hops); URL-normalization loops (trailing slash added and removed).","solutions":["Inspect the loop: catch the exception and print `e.response.history` / `e.response.headers['Location']` to see where it bounces, or request with `allow_redirects=False` to see the first hop.","Use a `requests.Session()` so cookies set during the redirect chain persist — auth/consent loops are the top cause.","Fix the request so the server stops redirecting: correct scheme/host, valid credentials or token, expected User-Agent header.","Only if the chain is legitimately long, raise the limit: `session.max_redirects = 60`."],"exampleFix":"# before\nr = requests.get('http://example.com/dashboard')  # bounces to /login forever (cookies not kept)\n# after\ns = requests.Session()\ns.post('https://example.com/login', data=creds)  # session keeps auth cookie\nr = s.get('https://example.com/dashboard')","handlingStrategy":"try-catch","validationCode":"# Bound or raise the limit deliberately per session\ns = requests.Session()\ns.max_redirects = 5","typeGuard":null,"tryCatchPattern":"try:\n    resp = session.get(url)\nexcept requests.exceptions.TooManyRedirects as e:\n    # likely a redirect loop; inspect the partial history\n    log.error('Redirect loop for %s: %s', url, [r.url for r in e.response.history] if e.response is not None else e)","preventionTips":["Treat TooManyRedirects as a redirect loop, not a transient failure — do not blindly retry","Set session.max_redirects to the smallest value your endpoints legitimately need","Use allow_redirects=False and follow Location manually when you need per-hop control or cookie/auth inspection","Log response.history when debugging to see the redirect chain"],"tags":["redirect","redirect-loop","session","cookies","python","requests"],"analyzedSha":"414f0513c33883adf6f2b46901d4f0b38a455851","analyzedAt":"2026-07-31T19:24:57.696Z","schemaVersion":2}