jdx/mise · error
too many redirects
Error message
too many redirects
What it means
The relay deliberately follows at most one download redirect: it re-sends the request to the redirect target once, and if the resulting response is still a 3xx it aborts with "too many redirects" instead of following further. This bounds the redirect chain so the relay never proxies open-redirect chains or loops.
Source
Thrown at src/github_relay.rs:685
|| !(asset_redirect(&url) || archive_redirect(&url, target.archive_repo.as_deref()))
{
bail!("unsupported redirect");
}
let redirect_host = url.host_str().unwrap_or_default().to_string();
let redirected_at = std::time::Instant::now();
let mut redirected = broker.client.request(parts.method.clone(), url);
for name in ["range", "if-range"] {
if let Some(value) = parts.headers.get(name) {
redirected = redirected.header(name, value);
}
}
response = redirected.send().await?;
if broker.audit.options.log_requests {
broker.audit.emit(serde_json::json!({"event": "request", "operation": format!("{} {redirect_host}/<download>", parts.method), "status": response.status().as_u16(), "headers_ms": redirected_at.elapsed().as_millis()}));
}
}
if response.status().is_redirection() {
bail!("too many redirects");
}
let mut builder = Response::builder().status(response.status());
for name in [
"content-type",
"content-length",
"content-range",
"etag",
"last-modified",
"link",
] {
if let Some(value) = response.headers().get(name) {
builder = builder.header(name, value);
}
}
let stream = futures_util::stream::try_unfold(
(response, permit, broker.cancel, broker.audit, deadline),
|(mut response, permit, cancel, audit, deadline)| async move {
let chunk = tokio::select! {View on GitHub (pinned to afd2eddd3a)
Solutions
- Retry the download so the relay obtains a fresh short-lived redirect link (stale links can bounce).
- Re-authenticate / refresh credentials if the redirect chain suggests an auth-driven redirect loop.
- Fetch the asset URL directly in the client if your path requires multi-hop redirects the relay intentionally won't follow.
Defensive patterns
Strategy: retry
Try / catch
match result {
Err(e) if e.to_string().contains("too many redirects") =>
retry_with_fresh_link(max_attempts = 2),
other => other,
} Prevention
- Fetch short-lived links and use them immediately; stale links tend to bounce.
- Retry downloads on failure — a fresh request re-resolves the redirect chain.
- Don't cache or reuse redirected download URLs across runs.
When it happens
Trigger: The post-redirect response itself carries a Location header (another 3xx status) — e.g. the asset host redirecting again (CDN hop chains), redirect loops, or an auth-expired redirect bouncing between hosts.
Common situations: GitHub asset hosts adding an extra CDN hop the relay's single-hop policy can't follow; expired/rotated short-lived archive links bouncing back to the API; misconfigured proxies injecting extra redirects.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- unsupported redirect
- unexpected request body
- remote action manifest ETag does not match its body
- Got HTML instead of text from {}
- cannot encode #{value.class} as JSON
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/e3e500d213540d8d.
Report an issue: GitHub.