{"record":{"id":"e3e500d213540d8d","repo":"jdx/mise","slug":"too-many-redirects","errorCode":null,"errorMessage":"too many redirects","messagePattern":"too many redirects","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/github_relay.rs","lineNumber":685,"sourceCode":"                || !(asset_redirect(&url) || archive_redirect(&url, target.archive_repo.as_deref()))\n            {\n                bail!(\"unsupported redirect\");\n            }\n            let redirect_host = url.host_str().unwrap_or_default().to_string();\n            let redirected_at = std::time::Instant::now();\n            let mut redirected = broker.client.request(parts.method.clone(), url);\n            for name in [\"range\", \"if-range\"] {\n                if let Some(value) = parts.headers.get(name) {\n                    redirected = redirected.header(name, value);\n                }\n            }\n            response = redirected.send().await?;\n            if broker.audit.options.log_requests {\n                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()}));\n            }\n        }\n        if response.status().is_redirection() {\n            bail!(\"too many redirects\");\n        }\n        let mut builder = Response::builder().status(response.status());\n        for name in [\n            \"content-type\",\n            \"content-length\",\n            \"content-range\",\n            \"etag\",\n            \"last-modified\",\n            \"link\",\n        ] {\n            if let Some(value) = response.headers().get(name) {\n                builder = builder.header(name, value);\n            }\n        }\n        let stream = futures_util::stream::try_unfold(\n            (response, permit, broker.cancel, broker.audit, deadline),\n            |(mut response, permit, cancel, audit, deadline)| async move {\n                let chunk = tokio::select! {","sourceCodeStart":667,"sourceCodeEnd":703,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/github_relay.rs#L667-L703","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"too many redirects\") =>\n        retry_with_fresh_link(max_attempts = 2),\n    other => other,\n}","preventionTips":["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."],"tags":["http","redirect","relay","limit-exceeded"],"backgroundTag":"request-timeout","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}