GopeedLab/gopeed · error

too many redirects

Error message

too many redirects

What it means

With the default redirect policy ('follow'), the injected fetch client aborts once a request chain exceeds 20 redirects (len(via) > 20). This mirrors browsers' safety cap and exists to stop redirect loops. The error propagates as a failed request wrapped in 'Network request failed: ...'.

Source

Thrown at pkg/download/engine/inject/stream/module.go:751

	reqBuilder.DisableAutoReadResponse()
	for _, header := range reqMeta.Headers {
		reqBuilder.SetHeader(header[0], header[1])
	}
	if body != nil && reqMeta.Method != http.MethodGet && reqMeta.Method != http.MethodHead {
		reqBuilder.SetBody(body)
		if contentType != "" && !hasHeader(reqMeta.Headers, "Content-Type") {
			reqBuilder.SetHeader("Content-Type", contentType)
		}
	}
	client.SetRedirectPolicy(func(req *http.Request, via []*http.Request) error {
		switch reqMeta.Redirect {
		case "manual":
			return http.ErrUseLastResponse
		case "error":
			return fmt.Errorf("redirect failed")
		default:
			if len(via) > 20 {
				return fmt.Errorf("too many redirects")
			}
			return nil
		}
	})
	resp, err := reqBuilder.Send(reqMeta.Method, reqMeta.URL)
	if err != nil {
		var ne net.Error
		if errorsAsTimeout(err, &ne) {
			return nil, fmt.Errorf("Network request timed out")
		}
		return nil, fmt.Errorf("Network request failed: %w", err)
	}
	id := fmt.Sprintf("%d", time.Now().UnixNano())
	meta := &fetchOpenMeta{
		ID:         id,
		Status:     resp.StatusCode,
		StatusText: resp.Status,
		URL:        reqMeta.URL,

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Enable cookie persistence for the request/session so auth redirects settle after login
  2. Use redirect:'manual' and follow Location yourself, stopping when the URL repeats
  3. Fix the server-side redirect loop or point the extension at the final canonical URL

Example fix

// before
const resp = await fetch(url); // 20+ hop loop -> too many redirects
// after
let target = url;
for (let i = 0; i < 20; i++) {
  const r = await fetch(target, { redirect: 'manual' });
  if (r.status >= 300 && r.status < 400 && r.headers['location']) {
    target = new URL(r.headers['location'], target).href;
    continue;
  }
  break; // final response
}
Defensive patterns

Strategy: validation

Validate before calling

// JS: detect a redirect loop before fetching
async function resolvesWithin(url, maxHops = 20) {
  const seen = new Set();
  let target = url;
  for (let i = 0; i < maxHops; i++) {
    if (seen.has(target)) return false; // loop detected
    seen.add(target);
    const r = await fetch(target, { redirect: 'manual' });
    const loc = r.headers && r.headers['location'];
    if (!(r.status >= 300 && r.status < 400) || !loc) return true;
    target = new URL(loc, target).href;
  }
  return false;
}

Try / catch

try {
  const resp = await fetch(url);
} catch (e) {
  if (String(e).includes('too many redirects')) {
    // stop retrying: deterministic loop; fix cookies/URL/server instead
  }
}

Prevention

When it happens

Trigger: A URL that bounces between two or more hosts indefinitely (A→B→A...) under default follow mode; cookies not being kept across hops so a auth endpoint keeps redirecting to login; http→https→http server misconfiguration.

Common situations: Signed/expiring redirect tokens that regenerate forever for cookie-less clients; CDN edge loops when the User-Agent is blocked; extensions hitting a mirror list where each mirror redirects to the next in a ring.

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/e0c90b56c07388e2. Report an issue: GitHub.