AlistGo/alist · error
stopped after 10 redirects
Error message
stopped after 10 redirects
What it means
'stopped after 10 redirects' is produced by the CheckRedirect policy on AList's shared HTTP client (internal/net/serve.go). Any request made through net.HttpClient() that follows more than 10 redirects is aborted with this error; the policy also strips the Referer header on each hop. It mirrors net/http's own default redirect cap but with an explicit error string.
Source
Thrown at internal/net/serve.go:272
}
all, _ := io.ReadAll(reader)
_ = res.Body.Close()
msg := string(all)
log.Debugln(msg)
return res, fmt.Errorf("http request [%s] failure,status: %d response:%s", URL, res.StatusCode, msg)
}
return res, nil
}
var once sync.Once
var httpClient *http.Client
func HttpClient() *http.Client {
once.Do(func() {
httpClient = NewHttpClient()
httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
req.Header.Del("Referer")
return nil
}
})
return httpClient
}
func NewHttpClient() *http.Client {
return &http.Client{
Timeout: time.Hour * 48,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify},
},
}
}
View on GitHub (pinned to 843d9dc814)
Solutions
- Fetch the URL with curl -L -v and count the hops; eliminate the loop or shorten the chain at the source
- Update the stored URL to the final destination after the redirect chain
- If the origin legitimately needs more hops, build a custom client with NewHttpClient() and a raised CheckRedirect limit instead of the shared one
- Check for cookie/session redirects that make each hop re-redirect, and carry cookies so the chain terminates
Example fix
// before
resp, err := net.HttpClient().Get(url)
// after
client := net.NewHttpClient()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) >= 20 { return errors.New("stopped after 20 redirects") }
req.Header.Del("Referer")
return nil
}
resp, err := client.Get(url) Defensive patterns
Strategy: retry
Validate before calling
# pre-resolve the URL outside the app to bound redirect chains
curl -sIL -o /dev/null -w '%{num_redirects}' <url> # keep only if <= 10 Type guard
func isRedirectLimit(err error) bool {
if ue, ok := err.(*url.Error); ok { err = ue.Err }
return err != nil && strings.Contains(err.Error(), "stopped after 10 redirects")
} Try / catch
resp, err := net.HttpClient().Get(u)
if isRedirectLimit(err) {
// resolve the final URL out-of-band and retry against it once
} Prevention
- Store final destination URLs, not short-links
- Keep a custom client with a higher cap for known-long chains
- Watch for redirect loops on origins you control
- Strip cookies/sessions that trigger hop amplification
When it happens
Trigger: Downloading or proxying a URL (offline download, remote fetch, any net.HttpClient() usage) whose chain of 3xx responses exceeds 10 hops; redirect loops (A->B->A...) between two URLs; a server that bounces between http/https or issues a session redirect on every request.
Common situations: Expired short-link services redirecting through many ad/interstitial pages; misconfigured origins with rewrite loops; offline-download URLs from link shorteners; CDNs repeatedly issuing trailing-slash redirects. Fixing the target URL or the origin's redirect logic is the real remedy.
Related errors
- unexpected status code: %d
- missing cookie or qrcode account
- resp.String()
- upload/download stream incomplete, possible network issue
- invalid range: failed to overlap
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/ae0518ec0d7d9a9e.
Report an issue: GitHub.