siyuan-note/siyuan · error

stopped after 10 redirects

Error message

stopped after 10 redirects

What it means

The plugin server's HTTP client sets CheckRedirect to cap redirects at 10; when a request follows more than 10 hops, net/http aborts with this error, which surfaces as the request's failure. It protects against redirect loops on plugin-facing proxy/fetches.

Solutions

  1. Verify the target URL and fix the server-side redirect loop
  2. Use the final https:// URL directly instead of one that redirects
  3. Reduce redirect hops or contact the endpoint owner; the 10-hop limit is intentional

Example fix

// before
fetch('http://example.com/data'); // redirects back and forth
// after
fetch('https://example.com/data'); // direct final URL
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try { const r = await client.get(url); } catch (e) { if (String(e).includes('stopped after 10 redirects')) { throw new Error('redirect loop at ' + url + '; use the final URL directly'); } throw e; }

Prevention

When it happens

Trigger: An outbound HTTP request from the plugin server (SSRF-safe transport) targets a URL whose server responds with a chain of more than 10 3xx redirects - typically a redirect loop or a very long redirect chain.

Common situations: Misconfigured target URL (http vs https ping-pong), auth-protected endpoints bouncing between login and original URL, or a looping short-link.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/43ab3ea22a4436c8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/plugin/server.go:237

		if !shouldProxyHeader(key, connectionHeaders) {
			continue
		}
		dst.Del(key)
		for _, value := range values {
			dst.Add(key, value)
		}
	}
}

func newProxyHTTPClient() *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			DialContext:        util.SSRFSafeDialer(30 * time.Second).DialContext,
			DisableCompression: true,
		},
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			if len(via) >= 10 {
				return fmt.Errorf("stopped after 10 redirects")
			}
			req.Header.Del("Referer")
			return nil
		},
		Timeout: 0,
	}
}

func writeProxyResponse(c *gin.Context, proxy *ResponseProxy) {
	if proxy.URL == "" {
		c.String(http.StatusBadRequest, "missing proxy url")
		return
	}
	targetURL, err := url.ParseRequestURI(proxy.URL)
	if err != nil {
		c.String(http.StatusBadRequest, "parse proxy url failed: %s", err.Error())
		return
	}

View on GitHub (pinned to 9f775e8a12)