larksuite/cli · error

too many redirects

Error message

too many redirects

What it means

This error comes from the CheckRedirect hook installed by NewDownloadHTTPClient in internal/validate/url.go. The download client intentionally limits how many HTTP redirects it will follow for untrusted source URLs; when a server responds with more redirects than opts.MaxRedirects (default 5), the client aborts and the http.Client surfaces 'stopped after N redirects' wrapping this error. It exists to prevent redirect loops and to bound the SSRF/validation surface.

Source

Thrown at internal/validate/url.go:144

// NewDownloadHTTPClient clones base client and enforces download-safe redirect
// and connection rules for untrusted URLs.
func NewDownloadHTTPClient(base *http.Client, opts DownloadHTTPClientOptions) *http.Client {
	if base == nil {
		base = &http.Client{}
	}
	if opts.MaxRedirects <= 0 {
		opts.MaxRedirects = defaultDownloadMaxRedirects
	}

	cloned := *base
	cloned.Transport = &downloadSchemeTransport{
		base:      cloneDownloadTransport(base.Transport),
		allowHTTP: opts.AllowHTTP,
	}
	cloned.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		if len(via) >= opts.MaxRedirects {
			return fmt.Errorf("too many redirects")
		}
		if len(via) > 0 {
			prev := via[len(via)-1]
			if strings.EqualFold(prev.URL.Scheme, "https") && strings.EqualFold(req.URL.Scheme, "http") {
				return fmt.Errorf("redirect from https to http is not allowed")
			}
		}
		if !opts.AllowHTTP && !strings.EqualFold(req.URL.Scheme, "https") {
			return fmt.Errorf("only https URLs are supported")
		}
		if err := ValidateDownloadSourceURL(req.Context(), req.URL.String()); err != nil {
			return fmt.Errorf("blocked redirect target: %w", err)
		}
		return nil
	}

	return &cloned
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Raise MaxRedirects in DownloadHTTPClientOptions when constructing the client (values <=0 fall back to the default of 5).
  2. Resolve the final URL manually (follow redirects with a plain client or inspect Location headers) and download the terminal URL directly.
  3. If it is a redirect loop, fix or avoid the source URL; no client option will make a looping server converge.

Example fix

// before
client := validate.NewDownloadHTTPClient(base, validate.DownloadHTTPClientOptions{})
// after (allow longer legitimate redirect chains)
client := validate.NewDownloadHTTPClient(base, validate.DownloadHTTPClientOptions{MaxRedirects: 10})
Defensive patterns

Strategy: try-catch

Validate before calling

u, err := url.Parse(src)
if err != nil || u.Scheme == "" {
    return fmt.Errorf("invalid download URL")
}
// Optionally pre-follow with a probe to detect loops before the real download.

Try / catch

resp, err := client.Get(url)
if err != nil {
    if strings.Contains(err.Error(), "too many redirects") {
        // treat source as unreliable: surface a clear message or try mirror
        return fmt.Errorf("download source redirect loop or chain too long: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Any download via a client built by NewDownloadHTTPClient where the target host responds with a 3xx chain longer than opts.MaxRedirects (default 5), including redirect loops where a server redirects back to itself or bounces between two URLs.

Common situations: Downloading from a misconfigured host whose http->https redirect points back to http (infinite loop); CDN or auth endpoints with long redirect chains; a caller setting MaxRedirects too low (e.g. 1-2) while the legit endpoint bounces a few times.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/d57334462720e19a. Report an issue: GitHub.