googleapis/mcp-toolbox · error

stopped after 10 redirects

Error message

stopped after 10 redirects

What it means

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.

Source

Thrown at internal/sources/http/http.go:356

					return fmt.Errorf("connection to blocked IP %s denied", ip)
				}
			}
			return nil
		},
	}

	if r, ok := resolver.(*net.Resolver); ok {
		dialer.Resolver = r
	}

	tr.DialContext = dialer.DialContext

	client := &http.Client{
		Timeout:   duration,
		Transport: tr,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			if len(via) >= 10 {
				return fmt.Errorf("stopped after 10 redirects")
			}

			hostname := req.URL.Hostname()
			if ip := net.ParseIP(hostname); ip != nil {
				if guard.IsIPBlocked(ip) {
					return fmt.Errorf("redirect to blocked IP %s denied", ip)
				}
				return nil
			}

			addrs, err := resolver.LookupHost(req.Context(), hostname)
			if err != nil {
				return fmt.Errorf("failed to resolve redirect host %s: %w", hostname, err)
			}

			for _, addr := range addrs {
				if ip := net.ParseIP(addr); ip != nil {
					if guard.IsIPBlocked(ip) {

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Fix the target URL so the server resolves the content without excessive redirects (curl -IL <url> to trace).
  2. Fix the server-side redirect loop (e.g. missing trailing slash, bad rewrite rules, or stale cookies causing auth bounce).
  3. If more than 10 hops are genuinely required, raise the limit in the CheckRedirect func in internal/sources/http/http.go.

Example fix

// before
client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error {
    if len(via) >= 10 { return fmt.Errorf("stopped after 10 redirects") } ... }}
// after: raise the cap if legitimately needed
    if len(via) >= 20 { return fmt.Errorf("stopped after 20 redirects") }
Defensive patterns

Strategy: try-catch

Validate before calling

const target = 'https://example.com/api';
const res = await fetch(target, { redirect: 'manual' });
let hops = 0, loc = res.headers.get('location');
while (loc && hops < 12) { hops++; const r = await fetch(new URL(loc, target), { redirect: 'manual' }); loc = r.headers.get('location'); }
if (hops >= 10) console.warn('target has a redirect loop; fix server before calling the tool');

Try / catch

try {
  const result = await callHttpTool(url);
} catch (err) {
  if (String(err).includes('stopped after 10 redirects')) {
    console.error('Redirect loop at target; inspect with curl -IL', url);
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: Misconfigured target server with a self-redirect loop; HTTP-to-HTTPS-to-HTTP ping-pong; auth pages that keep redirecting when cookies are dropped.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/fa49538024c42bb9. Report an issue: GitHub.