usememos/memos · warning

too many redirects

Error message

too many redirects

What it means

Returned by the redirect policy of the hardened HTTP client in internal/httpgetter when a URL chain exceeds 10 redirects (len(via) >= 10). Each hop is also re-validated against the internal-IP check, so redirect loops through public hosts terminate here with this error.

Source

Thrown at internal/httpgetter/html_meta.go:44

		KeepAlive: 30 * time.Second,
	}).DialContext
	httpClient = newHTTPClient()
)

func newHTTPClient() *http.Client {
	transport := http.DefaultTransport.(*http.Transport).Clone()
	transport.Proxy = nil
	transport.DialContext = secureDialContext

	return &http.Client{
		Transport: transport,
		Timeout:   5 * time.Second,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			if err := validateURL(req.URL.String()); err != nil {
				return errors.Wrap(err, "redirect to internal IP")
			}
			if len(via) >= 10 {
				return errors.New("too many redirects")
			}
			return nil
		},
	}
}

func secureDialContext(ctx context.Context, network, address string) (net.Conn, error) {
	host, port, err := net.SplitHostPort(address)
	if err != nil {
		return nil, errors.Wrap(err, "invalid address")
	}

	ips, err := resolveAllowedIPs(ctx, host)
	if err != nil {
		return nil, err
	}

	var dialErr error

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Resolve the final URL first with curl -IL <url> and use the destination directly
  2. If you own the server, fix the redirect loop (check https/http and trailing-slash canonicalization)
  3. Retry manually following each hop to identify and skip the looping hop

Example fix

// before
meta, err := httpgetter.GetHTMLMeta("http://a.co/loop")
// after (use final destination)
meta, err := httpgetter.GetHTMLMeta("https://example.com/final-page")
Defensive patterns

Strategy: try-catch

Validate before calling

// Optionally pre-flight with a client that counts redirects (Go does not expose this without doing the request)
// Instead, cap expectations client-side:
func likelyRedirectLoop(u string) bool {
  // heuristic: known shortener chains, or same host with alternating path
  return strings.Count(u, "://") > 0 && len(u) > 200 // very long chained short URLs
}

Try / catch

// Treat as non-fatal: skip the preview when redirect budget is exceeded
meta, err := getter.GetHTMLMeta(u)
if err != nil && strings.Contains(err.Error(), "too many redirects") {
  meta = &httpgetter.HTMLMeta{Title: u} // degrade gracefully
  err = nil
}

Prevention

When it happens

Trigger: Fetching a URL that redirects more than 10 times: login walls, ad/tracker chains, misconfigured server redirect loops (A -> B -> A), or shorteners stacked more than 10 deep.

Common situations: Pasting a corporate SSO or newsletter link into a memo (redirect through several trackers); a server misconfigured with an http<->https or trailing-slash loop; stale short links that bounce between mirrors.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/ded177c5bae65161. Report an issue: GitHub.