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 errorView on GitHub (pinned to 14d757ce1f)
Solutions
- Resolve the final URL first with curl -IL <url> and use the destination directly
- If you own the server, fix the redirect loop (check https/http and trailing-slash canonicalization)
- 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
- Store final destination URLs in notes rather than shortener chains
- Skip link preview when fetching fails; do not block note creation on preview errors
- If you own the redirecting server, cap chains and fix loops
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
- not a HTML page
- wrong image mediatype
- internal IP addresses are not allowed
- hostname resolved to no addresses
- invalid URL format
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/ded177c5bae65161.
Report an issue: GitHub.