{"record":{"id":"32c542f87e861384","repo":"tsenart/vegeta","slug":"stopped-after-d-redirects","errorCode":null,"errorMessage":"stopped after %d redirects","messagePattern":"stopped after (.+?) redirects","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/attack.go","lineNumber":149,"sourceCode":"}\n\n// ChunkedBody returns a functional option which makes the attacker send the\n// body of each request with the chunked transfer encoding.\nfunc ChunkedBody(b bool) func(*Attacker) {\n\treturn func(a *Attacker) { a.chunked = b }\n}\n\n// Redirects returns a functional option which sets the maximum\n// number of redirects an Attacker will follow.\nfunc Redirects(n int) func(*Attacker) {\n\treturn func(a *Attacker) {\n\t\ta.redirects = n\n\t\ta.client.CheckRedirect = func(_ *http.Request, via []*http.Request) error {\n\t\t\tswitch {\n\t\t\tcase n == NoFollow:\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\tcase n < len(via):\n\t\t\t\treturn fmt.Errorf(\"stopped after %d redirects\", n)\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Proxy returns a functional option which sets the `Proxy` field on\n// the http.Client's Transport\nfunc Proxy(proxy func(*http.Request) (*url.URL, error)) func(*Attacker) {\n\treturn func(a *Attacker) {\n\t\ttr := a.client.Transport.(*http.Transport)\n\t\ttr.Proxy = proxy\n\t}\n}\n\n// Timeout returns a functional option which sets the maximum amount of time\n// an Attacker will wait for a request to be responded to and completely read.","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/tsenart/vegeta/blob/cf5811269046c672a604b1eb352204d30f16ae4a/lib/attack.go#L131-L167","documentation":"When an attack is configured with MaxRedirects(n) where n != NoFollow, vegeta installs an http.Client.CheckRedirect that returns this error once the redirect chain exceeds n requests. It is a deliberate policy limit, not an internal failure — the attack records it and continues with subsequent requests.","triggerScenarios":"Hitting a target whose redirect chain is longer than the configured MaxRedirects(n), with n a positive number (not NoFollow/-1). E.g. MaxRedirects(5) against an endpoint redirecting 6+ times.","commonSituations":"Redirect loops caused by misconfigured TLS/hosts (http->https bouncing), cookie-less clients re-triggering redirects, load balancers redirecting repeatedly, or simply setting the limit too low.","solutions":["Increase the limit with a higher MaxRedirects(n) to accommodate the target's chain length.","Use MaxRedirects(-1) (NoFollow) to not follow redirects at all (returns the first non-redirect response instead of this error).","Fix the server-side redirect loop (scheme, trailing slash, cookie/Host handling).","Inspect recorded responses' Location headers to distinguish a loop from a deep chain."],"exampleFix":"// before\nattacker.MaxRedirects(2)\n// after\nattacker.MaxRedirects(10) // or attacker.MaxRedirects(vegeta.NoFollow)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"for res := range results {\n\tif res.Code == 0 && res.Error != \"\" {\n\t\tif strings.Contains(res.Error, \"stopped after\") {\n\t\t\t// redirect limit hit: raise MaxRedirects or use NoFollow\n\t\t\tcontinue\n\t\t}\n\t}\n\t// handle other errors...\n}","preventionTips":["Set MaxRedirects to a value above the target's known redirect chain length.","Use MaxRedirects(NoFollow) if you want the first non-redirect response.","Watch for redirect loops (same Location repeating) and fix server config.","Treat this error string as a signal to inspect the Location chain, not a crash."],"tags":["http","redirects","load-testing"],"backgroundTag":"too-many-redirects","analyzedSha":"cf5811269046c672a604b1eb352204d30f16ae4a","analyzedAt":"2026-08-31T11:04:20.464Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}