{"record":{"id":"fa49538024c42bb9","repo":"googleapis/mcp-toolbox","slug":"stopped-after-10-redirects","errorCode":null,"errorMessage":"stopped after 10 redirects","messagePattern":"stopped after 10 redirects","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/http/http.go","lineNumber":356,"sourceCode":"\t\t\t\t\treturn fmt.Errorf(\"connection to blocked IP %s denied\", ip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tif r, ok := resolver.(*net.Resolver); ok {\n\t\tdialer.Resolver = r\n\t}\n\n\ttr.DialContext = dialer.DialContext\n\n\tclient := &http.Client{\n\t\tTimeout:   duration,\n\t\tTransport: tr,\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\tif len(via) >= 10 {\n\t\t\t\treturn fmt.Errorf(\"stopped after 10 redirects\")\n\t\t\t}\n\n\t\t\thostname := req.URL.Hostname()\n\t\t\tif ip := net.ParseIP(hostname); ip != nil {\n\t\t\t\tif guard.IsIPBlocked(ip) {\n\t\t\t\t\treturn fmt.Errorf(\"redirect to blocked IP %s denied\", ip)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\taddrs, err := resolver.LookupHost(req.Context(), hostname)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to resolve redirect host %s: %w\", hostname, err)\n\t\t\t}\n\n\t\t\tfor _, addr := range addrs {\n\t\t\t\tif ip := net.ParseIP(addr); ip != nil {\n\t\t\t\t\tif guard.IsIPBlocked(ip) {","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/http/http.go#L338-L374","documentation":"The HTTP source wraps its outbound client with a CheckRedirect hook that aborts any redirect chain longer than 10 hops, matching net/http's default behavior. It returns this error from the CheckRedirect callback so the client stops following redirects. This prevents infinite redirect loops and SSRF amplification.","triggerScenarios":"Any http.Client request via this source whose server responds with a chain of more than 10 consecutive 3xx redirects (Location headers), e.g. a redirect loop between two URLs.","commonSituations":"Misconfigured target server with a self-redirect loop; HTTP-to-HTTPS-to-HTTP ping-pong; auth pages that keep redirecting when cookies are dropped.","solutions":["Fix the target URL so the server resolves the content without excessive redirects (curl -IL <url> to trace).","Fix the server-side redirect loop (e.g. missing trailing slash, bad rewrite rules, or stale cookies causing auth bounce).","If more than 10 hops are genuinely required, raise the limit in the CheckRedirect func in internal/sources/http/http.go."],"exampleFix":"// before\nclient := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error {\n    if len(via) >= 10 { return fmt.Errorf(\"stopped after 10 redirects\") } ... }}\n// after: raise the cap if legitimately needed\n    if len(via) >= 20 { return fmt.Errorf(\"stopped after 20 redirects\") }","handlingStrategy":"try-catch","validationCode":"const target = 'https://example.com/api';\nconst res = await fetch(target, { redirect: 'manual' });\nlet hops = 0, loc = res.headers.get('location');\nwhile (loc && hops < 12) { hops++; const r = await fetch(new URL(loc, target), { redirect: 'manual' }); loc = r.headers.get('location'); }\nif (hops >= 10) console.warn('target has a redirect loop; fix server before calling the tool');","typeGuard":null,"tryCatchPattern":"try {\n  const result = await callHttpTool(url);\n} catch (err) {\n  if (String(err).includes('stopped after 10 redirects')) {\n    console.error('Redirect loop at target; inspect with curl -IL', url);\n  } else throw err;\n}","preventionTips":["Trace redirect chains with curl -IL before wiring a URL into the tool config.","Avoid pointing tools at URLs known to bounce auth (drop cookies on 3xx).","Monitor for repeated redirect errors from one host — a sign of a server-side loop."],"tags":["http","redirect","network"],"backgroundTag":"too-many-redirects","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}