apple/pkl · error · HttpClientException
httpTooManyRedirects
httpTooManyRedirects
Error message
httpTooManyRedirects: ${max} What it means
Pkl's HTTP client followed the maximum number of HTTP redirects (MAX_HTTP_REDIRECTS) without reaching a final response. doSend tracks redirectCount across responses and throws httpTooManyRedirects when the limit is reached, as a guard against redirect loops.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/http/RequestRewritingClient.java:126
private <T> HttpResponse<T> doSend(
HttpRequest request,
BodyHandler<T> responseBodyHandler,
HttpRequestChecker httpRequestChecker)
throws SecurityManagerException, IOException {
var redirectCount = 0;
var currentRequestUri = rewriteUri(request.uri());
var currentRequest = rewriteRequest(request, currentRequestUri);
while (true) {
httpRequestChecker.check(currentRequestUri);
var response = delegate.send(currentRequest, responseBodyHandler, httpRequestChecker);
if (!HttpUtils.isRedirectStatusCode(response.statusCode())) {
return response;
}
if (response.body() instanceof Closeable closeable) {
closeable.close();
}
if (redirectCount >= MAX_HTTP_REDIRECTS) {
throw new HttpClientException(
ErrorMessages.create("httpTooManyRedirects", MAX_HTTP_REDIRECTS));
}
var location = response.headers().firstValue("Location");
if (location.isEmpty()) {
throw new HttpClientException(
ErrorMessages.create("httpRedirectNoLocation", currentRequestUri));
}
URI redirectUri;
try {
redirectUri = currentRequestUri.resolve(location.get());
} catch (IllegalArgumentException e) {
throw new HttpClientException(
ErrorMessages.create("httpRedirectInvalidUri", currentRequestUri, location.get()));
}
if (currentRequestUri.getScheme().equalsIgnoreCase("https")
&& redirectUri.getScheme().equalsIgnoreCase("http")) {
throw new HttpClientException(
ErrorMessages.create("httpRedirectCannotDowngrade", currentRequestUri, redirectUri));View on GitHub (pinned to f3efcbfc9b)
Solutions
- Open the URL in curl (`curl -IL <url>`) and inspect the Location headers to find the redirect loop.
- Fix the loop on the server side (rewrite rules, trailing-slash handling, http-to-https configuration).
- If the target moved, update the Pkl import/dependency URL to the final destination.
- Check for a proxy rewriting URLs into a cycle (see no-proxy/proxy config).
- As a workaround, serve the content at a stable, non-redirecting URL.
Example fix
// before (nginx)
rewrite ^/pkg/$ /pkg permanent; # /pkg -> /pkg/ -> /pkg loop
// after (nginx)
location /pkg { try_files $uri $uri/ =404; } Defensive patterns
Strategy: retry
Validate before calling
HttpURLConnection c = (HttpURLConnection) URI.create(targetUrl).toURL().openConnection();
c.setInstanceFollowRedirects(false);
int code = c.getResponseCode();
Set<String> seen = new HashSet<>();
while (300 <= code && code < 400) {
String loc = c.getHeaderField("Location");
if (loc == null || !seen.add(loc)) throw new IllegalStateException("Redirect loop at " + loc);
c = (HttpURLConnection) URI.create(loc).toURL().openConnection();
code = c.getResponseCode();
} Try / catch
try {
// fetch module via Pkl
} catch (HttpClientException e) {
if (e.getMessage().startsWith("httpTooManyRedirects")) {
// diagnose loop with curl -IL, point the import at the final URL
}
} Prevention
- Curl -IL your import URLs in CI to detect redirect loops early.
- Pin dependencies to stable, non-redirecting URLs.
- Fix trailing-slash and http->https rewrite loops on servers you control.
- Check load balancer redirect rules when a URL bounces between hosts.
When it happens
Trigger: A Pkl `import`/module fetch over HTTP encounters a server (or chain of servers) whose responses keep redirecting — typically a redirect loop (A -> B -> A) or a chain longer than MAX_HTTP_REDIRECTS. doSend throws after closing each interim response body.
Common situations: A misconfigured web server redirecting between trailing-slash and non-trailing-slash URLs forever, http<->https ping-pong caused by conflicting TLS settings, a load balancer loop, or an extremely long redirect chain.
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
- httpRedirectNoLocation
- errorConnectingToHost
- ioErrorMakingHttpGet
- badHttpStatusCode
- unableToAccessPublishedPackage
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/b0720d670bd97142.
Report an issue: GitHub.