{"record":{"id":"d57334462720e19a","repo":"larksuite/cli","slug":"too-many-redirects","errorCode":null,"errorMessage":"too many redirects","messagePattern":"too many redirects","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/validate/url.go","lineNumber":144,"sourceCode":"\n// NewDownloadHTTPClient clones base client and enforces download-safe redirect\n// and connection rules for untrusted URLs.\nfunc NewDownloadHTTPClient(base *http.Client, opts DownloadHTTPClientOptions) *http.Client {\n\tif base == nil {\n\t\tbase = &http.Client{}\n\t}\n\tif opts.MaxRedirects <= 0 {\n\t\topts.MaxRedirects = defaultDownloadMaxRedirects\n\t}\n\n\tcloned := *base\n\tcloned.Transport = &downloadSchemeTransport{\n\t\tbase:      cloneDownloadTransport(base.Transport),\n\t\tallowHTTP: opts.AllowHTTP,\n\t}\n\tcloned.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\tif len(via) >= opts.MaxRedirects {\n\t\t\treturn fmt.Errorf(\"too many redirects\")\n\t\t}\n\t\tif len(via) > 0 {\n\t\t\tprev := via[len(via)-1]\n\t\t\tif strings.EqualFold(prev.URL.Scheme, \"https\") && strings.EqualFold(req.URL.Scheme, \"http\") {\n\t\t\t\treturn fmt.Errorf(\"redirect from https to http is not allowed\")\n\t\t\t}\n\t\t}\n\t\tif !opts.AllowHTTP && !strings.EqualFold(req.URL.Scheme, \"https\") {\n\t\t\treturn fmt.Errorf(\"only https URLs are supported\")\n\t\t}\n\t\tif err := ValidateDownloadSourceURL(req.Context(), req.URL.String()); err != nil {\n\t\t\treturn fmt.Errorf(\"blocked redirect target: %w\", err)\n\t\t}\n\t\treturn nil\n\t}\n\n\treturn &cloned\n}","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/validate/url.go#L126-L162","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Raise MaxRedirects in DownloadHTTPClientOptions when constructing the client (values <=0 fall back to the default of 5).","Resolve the final URL manually (follow redirects with a plain client or inspect Location headers) and download the terminal URL directly.","If it is a redirect loop, fix or avoid the source URL; no client option will make a looping server converge."],"exampleFix":"// before\nclient := validate.NewDownloadHTTPClient(base, validate.DownloadHTTPClientOptions{})\n// after (allow longer legitimate redirect chains)\nclient := validate.NewDownloadHTTPClient(base, validate.DownloadHTTPClientOptions{MaxRedirects: 10})","handlingStrategy":"try-catch","validationCode":"u, err := url.Parse(src)\nif err != nil || u.Scheme == \"\" {\n    return fmt.Errorf(\"invalid download URL\")\n}\n// Optionally pre-follow with a probe to detect loops before the real download.","typeGuard":null,"tryCatchPattern":"resp, err := client.Get(url)\nif err != nil {\n    if strings.Contains(err.Error(), \"too many redirects\") {\n        // treat source as unreliable: surface a clear message or try mirror\n        return fmt.Errorf(\"download source redirect loop or chain too long: %w\", err)\n    }\n    return err\n}","preventionTips":["Set MaxRedirects explicitly to match known endpoint redirect depth instead of relying on the default of 5.","Test download sources once at setup time to catch redirect loops early.","Prefer direct canonical URLs (CDN/final destination) over endpoints that bounce."],"tags":["network","http","redirect","ssrf-protection"],"backgroundTag":"too-many-redirects","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}