GopeedLab/gopeed · error

redirect failed

Error message

redirect failed

What it means

In the injected fetch implementation, reqMeta.Redirect maps to the fetch() 'redirect' option. When the extension sets redirect:"error", the client's redirect policy returns this error for ANY 3xx response, mirroring the browser fetch spec where redirect:'error' rejects on a redirect. The error surfaces as a failed fetch wrapped in 'Network request failed'.

Source

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

	}()
	reqBuilder := client.R()
	reqBuilder.SetContext(ctx)
	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,

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Use the default redirect:'follow' when you want the final response
  2. Use redirect:'manual' to inspect the 3xx and its Location header yourself
  3. Resolve the target URL first (follow once manually) and pass the final URL with redirect:'error'

Example fix

// before
const resp = await fetch(url, { redirect: 'error' }); // 302 -> redirect failed
// after
const resp = await fetch(url, { redirect: 'follow' }); // or 'manual' to read Location
Defensive patterns

Strategy: validation

Validate before calling

// JS: choose redirect mode deliberately before fetching
const REDIRECT = new Set(['follow', 'manual', 'error']);
const mode = REDIRECT.has(opts.redirect) ? opts.redirect : 'follow';
if (mode === 'error') {
  // preflight: a HEAD probe tells you if the URL redirects before you commit
  const probe = await fetch(url, { method: 'HEAD', redirect: 'manual' });
  if (probe.status >= 300 && probe.status < 400) throw new Error('URL redirects; use follow/manual');
}
const resp = await fetch(url, { redirect: mode });

Try / catch

try {
  const resp = await fetch(url, { redirect: 'error' });
} catch (e) {
  if (String(e).includes('redirect failed')) {
    const resp = await fetch(url, { redirect: 'manual' }); // fall back to manual handling
  }
}

Prevention

When it happens

Trigger: gopeed.fetch(url, {redirect: 'error'}) where the server answers 301/302/303/307/308; resolving a short link or CDN URL that always redirects to a signed target while redirect mode is 'error'.

Common situations: Extension authors copying browser code that used redirect:'error' to detect hops; sites where the initial resolve URL is a permanent redirect to the canonical host; login flows that redirect to an auth domain.

Related errors


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