dgtlmoon/changedetection.io · error · Exception
Too many redirects
Error message
Too many redirects
What it means
The requests fetcher caps manual redirect following; when the loop exits without a non-redirect response (redirect chain exceeds the attempt limit) it raises 'Too many redirects'.
Source
Thrown at changedetectionio/content_fetchers/requests.py:126
current_url = url
for _ in range(10):
if not r.is_redirect:
break
location = r.headers.get('Location', '')
redirect_url = urljoin(current_url, location)
if not allow_iana_restricted:
if is_url_private_or_parser_confused(redirect_url):
raise Exception(f"Redirect blocked: '{redirect_url}' resolves to a private/reserved IP address "
f"or contains a parser-differential payload.")
current_url = redirect_url
r = session.request('GET', redirect_url,
headers=request_headers,
timeout=timeout,
proxies=proxies,
verify=False,
allow_redirects=False)
else:
raise Exception("Too many redirects")
except Exception as e:
msg = str(e)
if proxies and 'SOCKSHTTPSConnectionPool' in msg:
msg = f"Proxy connection failed? {msg}"
raise Exception(msg) from e
# If the response did not tell us what encoding format to expect, Then use chardet to override what `requests` thinks.
# For example - some sites don't tell us it's utf-8, but return utf-8 content
# This seems to not occur when using webdriver/selenium, it seems to detect the text encoding more reliably.
# https://github.com/psf/requests/issues/1604 good info about requests encoding detection
if not is_binary:
# Don't run this for PDF (and requests identified as binary) takes a _long_ time
if not r.headers.get('content-type') or not 'charset=' in r.headers.get('content-type'):
# For XML/RSS feeds, check the XML declaration for encoding attribute
# This is more reliable than chardet which can misdetect UTF-8 as MacRoman
content_type = r.headers.get('content-type', '').lower()
if 'xml' in content_type or 'rss' in content_type:View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Trace the chain: curl -IL <url> and look for the loop point
- Add required cookies/headers or a User-Agent the site expects
- If the loop is on the server side, watch the final destination URL directly
Defensive patterns
Strategy: retry
Validate before calling
import requests
r = requests.get(url, allow_redirects=False, timeout=10)
seen = set()
while r.is_redirect:
nxt = r.headers['Location']
if nxt in seen:
raise ConfigError('redirect loop detected — fix watch URL')
seen.add(nxt)
r = requests.get(nxt, allow_redirects=False, timeout=10) Try / catch
try:
fetcher.run()
except Exception as e:
if 'Too many redirects' in str(e):
trace_chain(url) # find the loop and update watch to final URL Prevention
- Watch the final destination URL, not redirecting front doors
- Send the session cookies/headers the site expects to break consent loops
- Detect redirect loops in config before scheduling checks
When it happens
Trigger: URL whose redirect chain never terminates — A→B→A loops, cookie-less client stuck on a consent/redirect page, or server always answering 30x.
Common situations: Site requiring cookies/headers the fetcher lacks so it loops on the same redirect; misconfigured server; intentional redirect loop.
Related errors
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/952816f6820ef2ae.
Report an issue: GitHub.