{"record":{"id":"912e24be123c4dfb","repo":"HMCL-dev/HMCL","slug":"too-much-redirects","errorCode":null,"errorMessage":"Too much redirects","messagePattern":"Too much redirects","errorType":"exception","errorClass":"java.io.IOException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java","lineNumber":352,"sourceCode":"                            headers.forEach(connection::setRequestProperty);\n                            responseCode = connection.getResponseCode();\n                            responseInfo = UrlResponseInfo.of(connection);\n\n                            bmclapiHash = responseInfo.headers().firstValue(\"x-bmclapi-hash\").orElse(null);\n                            if (DigestUtils.isSha1Digest(bmclapiHash)) {\n                                Optional<Path> cache = repository.checkExistentFile(null, \"SHA-1\", bmclapiHash);\n                                if (cache.isPresent()) {\n                                    useCachedResult(cache.get());\n                                    LOG.info(\"Using cached file for \" + NetworkUtils.dropQuery(uri));\n                                    return;\n                                }\n                            }\n\n                            if (responseCode >= 300 && responseCode <= 308 && responseCode != 306 && responseCode != 304) {\n                                if (redirects == null) {\n                                    redirects = new ArrayList<>();\n                                } else if (redirects.size() >= 20) {\n                                    throw new IOException(\"Too much redirects\");\n                                }\n\n                                String location = connection.getHeaderField(\"Location\");\n                                if (StringUtils.isBlank(location))\n                                    throw new IOException(\"Redirected to an empty location\");\n\n                                URI target = currentURI.resolve(NetworkUtils.encodeLocation(location));\n                                redirects.add(target);\n\n                                if (!NetworkUtils.isHttpUri(target))\n                                    throw new IOException(\"Redirected to not http URI: \" + target);\n\n                                currentURI = target;\n                            } else {\n                                keepConnection = true;\n                                break;\n                            }\n                        } finally {","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java#L334-L370","documentation":"FetchTask.downloadHttp() follows HTTP 3xx redirects manually and caps the chain at 20 hops to prevent infinite redirect loops. This IOException is thrown when the server has redirected more than 20 times, indicating a redirect loop or pathological configuration.","triggerScenarios":"A URL whose redirect chain exceeds 20 hops — typically an infinite redirect loop (A -> B -> A), a misconfigured CDN, or cookie/auth issues causing repeated redirects to login and back.","commonSituations":"Mirror servers misconfigured with redirect loops; auth-gated endpoints bouncing between login and target; reverse proxies with conflicting rewrite rules.","solutions":["Open the URL in a browser or with curl -IL to inspect the redirect chain and find the loop","Fix the target server/proxy configuration causing the loop, or use a direct final URL","Authenticate properly if redirects are caused by missing cookies/credentials","Clear any cached stale URL and use the canonical endpoint"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-check redirect chain before downloading\nHttpURLConnection c = (HttpURLConnection) uri.toURL().openConnection();\nc.setInstanceFollowRedirects(false);\nint hops = 0;\nwhile (c.getResponseCode() >= 300 && c.getResponseCode() <= 308 && ++hops <= 25)\n    c = (HttpURLConnection) new URI(c.getHeaderField(\"Location\")).toURL().openConnection();\nif (hops > 20) throw new IllegalStateException(\"Redirect loop at \" + uri);","typeGuard":null,"tryCatchPattern":"try {\n    fetchTask.run();\n} catch (IOException e) {\n    if (e.getMessage().equals(\"Too much redirects\")) useDirectUrlOrFallbackMirror();\n    else throw e;\n}","preventionTips":["Resolve final URLs once and cache them instead of chasing redirects each run","Curl -I suspect mirrors to detect loops early","Avoid endpoints with auth-redirect bounce patterns"],"tags":["http","redirect","network","loop"],"backgroundTag":"http-error-response","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}