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

  1. Verify the request targets a real release asset or archive endpoint (repos/.../releases/assets/... or .../tarball|zipball/...).
  2. Check authentication — unauthenticated requests often redirect to HTML error pages rather than the asset host.
  3. Confirm target.git is false for download requests; git targets cannot follow download redirects through the relay.
  4. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/fb00dd9b64cd1bc4. Report an issue: GitHub.