jhy/jsoup · error · IOException
Too many redirects occurred trying to load URL
Error message
Too many redirects occurred trying to load URL %s
What it means
Jsoup caps redirect chains at MAX_REDIRECTS to protect against infinite or circular redirect loops. When following redirects exceeds that cap, execute() throws IOException with this message, including the URL of the last response. It mirrors the same safeguard in browsers and HTTP clients.
Solutions
- Open the URL in a browser or curl -IL to find and fix the redirect loop server-side
- Manually follow redirects with followRedirects(false) and stop after detecting a repeated URL
- Send required cookies/auth headers so the server stops redirecting to login
- If legitimate deep chains exist, resend from the last Location with your own counter
Example fix
// before
Document doc = Jsoup.connect(url).get(); // IOException on loop
// after
Connection.Response res = Jsoup.connect(url).followRedirects(false).execute();
Set<String> seen = new HashSet<>();
while (res.statusCode() / 100 == 3 && seen.add(res.url().toString())) {
res = Jsoup.connect(res.header("Location")).followRedirects(false).execute();
} Defensive patterns
Strategy: try-catch
Try / catch
try { doc = Jsoup.connect(url).get(); } catch (IOException e) { if (e.getMessage().startsWith("Too many redirects")) { /* flag URL as looping */ } } Prevention
- Manually follow redirects with a seen-URL set to detect loops early
- Persist session cookies so login redirects complete
- Fix server-side rewrite loops surfaced by this error
When it happens
Trigger: A server redirect loop (A -> B -> A), misconfigured load balancers bouncing requests, or cookies/auth never accepted so the server keeps redirecting to a login page that redirects back.
Common situations: >20-deep redirect chains; www/non-www or http/https rewrite loops; login redirects that fail because session cookies are not persisted; geo/consent walls that never settle.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- You must execute the request before getting a response.
- URL not set. Make sure to call #url(...) before executing…
- Cannot follow redirect with a streamed request body…
- HTTP error fetching URL
- Unhandled content type. Must be a text or XML media type
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/46cabd8d32201433.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/helper/HttpConnection.java:1204
}
// set up url, method, header, cookies
void prepareResponse(Map<String, List<String>> resHeaders, HttpConnection.@Nullable Response previousResponse) throws IOException {
processResponseHeaders(resHeaders); // includes cookie key/val read during header scan
CookieUtil.storeCookies(req, this, url, resHeaders); // add set cookies to cookie store
if (previousResponse != null) { // was redirected
// map previous response cookies into this response cookies() object
for (Map.Entry<String, String> prevCookie : previousResponse.cookies().entrySet()) {
if (!hasCookie(prevCookie.getKey()))
cookie(prevCookie.getKey(), prevCookie.getValue());
}
previousResponse.safeClose();
// enforce too many redirects:
numRedirects = previousResponse.numRedirects + 1;
if (numRedirects >= MAX_REDIRECTS)
throw new IOException(String.format("Too many redirects occurred trying to load URL %s", previousResponse.url()));
}
}
void processResponseHeaders(Map<String, List<String>> resHeaders) {
for (Map.Entry<String, List<String>> entry : resHeaders.entrySet()) {
String name = entry.getKey();
if (name == null)
continue; // http/1.1 line
List<String> values = entry.getValue();
for (String value : values) {
addHeader(name, fixHeaderEncoding(value));
}
}
}
/**
Servers may encode response headers in UTF-8 instead of RFC defined 8859. The JVM decodes the headers (before we see them) as 8859, which can lead to mojibake data.View on GitHub (pinned to 9851ac5d9c)