jdx/mise · error
unsupported redirect
Error message
unsupported redirect
What it means
After following the initial GitHub API response's Location header, the relay validates that the redirect target is one it is allowed to proxy: it must not be a git target, and it must be either a release-asset redirect or an archive redirect for the expected archive repo. Anything else (arbitrary hosts, HTML pages, git endpoints) is rejected with "unsupported redirect" to prevent the relay from proxying to unintended destinations.
Source
Thrown at src/github_relay.rs:669
let mut response = req.body(body).send().await?;
if broker.audit.options.log_requests {
broker.audit.emit(serde_json::json!({"event": "request", "operation": operation, "status": response.status().as_u16(), "headers_ms": sent_at.elapsed().as_millis()}));
}
// Only asset redirects are followed. Preserve resume headers, never credentials.
for _ in 0..3 {
if !response.status().is_redirection() {
break;
}
let location = response
.headers()
.get("location")
.ok_or_else(|| eyre::eyre!("missing redirect"))?
.to_str()?;
let url = Url::parse(location)?;
if target.git
|| !(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());View on GitHub (pinned to afd2eddd3a)
Solutions
- Verify the request targets a real release asset or archive endpoint (repos/.../releases/assets/... or .../tarball|zipball/...).
- Check authentication — unauthenticated requests often redirect to HTML error pages rather than the asset host.
- Confirm target.git is false for download requests; git targets cannot follow download redirects through the relay.
- Update the relay's asset_redirect/archive_redirect allowlist if GitHub changed its redirect hosts.
Defensive patterns
Strategy: try-catch
Try / catch
match result {
Err(e) if e.to_string().contains("unsupported redirect") => {
// refresh auth and retry once, or download directly from the resolved asset URL
}
other => other?,
} Prevention
- Always send valid GitHub credentials so the API returns asset/archive redirects, not HTML error pages.
- Request only known download endpoints (releases/assets, tarball, zipball).
- Keep the relay updated for new GitHub redirect hosts.
When it happens
Trigger: The upstream GitHub API returns a 3xx whose Location is neither a recognized release-asset URL nor an archive URL for target.archive_repo — e.g. GitHub redirecting to an error/login page, a changed asset host not covered by asset_redirect, or a request issued on a git target.
Common situations: Requesting assets from repos whose release asset URLs changed shape; API responses redirecting to an unexpected host (rate-limit or auth HTML pages); using the relay on a git:// target where downloads aren't allowed; GitHub introducing a new redirect domain the relay doesn't whitelist.
Related errors
- too many redirects
- unexpected request body
- unsupported relay destination
- refusing to send credentials over an HTTPS-to-HTTP URL repla
- remote action manifest ETag does not match its body
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/fb00dd9b64cd1bc4.
Report an issue: GitHub.