{"record":{"id":"6160b1ae4a36fa47","repo":"Tencent/WeKnora","slug":"stopped-after-d-redirects","errorCode":null,"errorMessage":"stopped after %d redirects","messagePattern":"stopped after (.+?) redirects","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/security.go","lineNumber":708,"sourceCode":"// redirect policy — those live on the *http.Client — so a single transport can\n// be shared across many clients to pool keep-alive connections globally.\nfunc NewSSRFSafeTransport(config SSRFSafeHTTPClientConfig) *http.Transport {\n\treturn &http.Transport{\n\t\tDisableKeepAlives:  config.DisableKeepAlives,\n\t\tDisableCompression: config.DisableCompression,\n\t\t// Dial with SSRF protection - validates resolved IPs before connecting\n\t\tDialContext: SSRFSafeDialContext,\n\t}\n}\n\n// newSSRFCheckRedirect returns a CheckRedirect policy that enforces the redirect\n// count limit, strips sensitive headers on cross-host hops, and re-validates\n// every redirect target against SSRF protections.\nfunc newSSRFCheckRedirect(maxRedirects int) func(*http.Request, []*http.Request) error {\n\treturn func(req *http.Request, via []*http.Request) error {\n\t\t// Check redirect count\n\t\tif len(via) >= maxRedirects {\n\t\t\treturn fmt.Errorf(\"stopped after %d redirects\", maxRedirects)\n\t\t}\n\n\t\t// Strip credentials when the redirect crosses hosts so connector\n\t\t// tokens (e.g. Yuque X-Auth-Token) cannot leak to a third party.\n\t\tif len(via) > 0 && !sameHTTPOrigin(via[0].URL, req.URL) {\n\t\t\tstripRedirectSensitiveHeaders(req)\n\t\t}\n\n\t\t// Validate the redirect target URL for SSRF (whitelist-aware).\n\t\t// Even whitelisted hosts must use http/https to prevent scheme-based attacks.\n\t\tredirectScheme := strings.ToLower(req.URL.Scheme)\n\t\tif redirectScheme != \"http\" && redirectScheme != \"https\" {\n\t\t\treturn fmt.Errorf(\"%w: invalid scheme %s\", ErrSSRFRedirectBlocked, redirectScheme)\n\t\t}\n\t\tredirectHost := req.URL.Hostname()\n\t\tif redirectHost != \"\" && IsSSRFWhitelisted(redirectHost) {\n\t\t\treturn nil\n\t\t}","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L690-L726","documentation":"This error comes from the CheckRedirect policy (newSSRFCheckRedirect) when a request chain exceeds maxRedirects hops — mirroring net/http's own 'stopped after N redirects' behavior. It means the server is redirecting in a loop or an excessive chain, and the client aborts rather than following indefinitely.","triggerScenarios":"Any request through an SSRF-safe client configured with newSSRFCheckRedirect(maxRedirects) where the server responds with more than maxRedirects consecutive 3xx responses.","commonSituations":"Redirect loops caused by cookie-less auth bouncing between login and target, misconfigured reverse proxies alternating redirects, or http→https→www chains longer than the limit.","solutions":["Increase maxRedirects if the chain is legitimately long.","Break the redirect loop: fix the server/proxy configuration causing repeated 3xxs.","Request the final URL directly instead of following a long chain.","Send required auth/cookies so the server stops redirecting."],"exampleFix":"// before\nclient := &http.Client{Transport: base}\n// after\nclient := &http.Client{\n    Transport: base,\n    CheckRedirect: secutils.NewSSRFCheckRedirect(10), // raise limit\n}","handlingStrategy":"retry","validationCode":"// no pre-call validation possible; bounded by client config\nmaxRedirects := 10 // set explicitly when constructing the client","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"stopped after\") {\n    return fmt.Errorf(\"redirect loop or chain too long for %s: %w\", req.URL, err)\n}","preventionTips":["Set an explicit, generous maxRedirects (e.g. 10).","Fix server-side redirect loops (auth bounce, proxy alternation).","Request the canonical final URL directly when known.","Ensure cookies/auth headers survive so the server stops redirecting."],"tags":["http","redirect","network"],"backgroundTag":"too-many-redirects","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}