siyuan-note/siyuan · warning
stopped after 10 redirects
Error message
stopped after 10 redirects
What it means
Returned by the plugin proxy HTTP client's CheckRedirect when an outbound proxied request exceeded 10 redirects (len(via) >= 10). This is the standard net/http redirect-loop guard, applied to the plugin server's proxy egress which uses SSRFSafeDialer. It prevents infinite redirect chains / open-redirect abuse.
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 251596fc0d)
Solutions
- Resolve the final URL server-side and configure the plugin to target it directly (fewer hops).
- Check the target host for a redirect loop (auth, http->https, www<->apex).
- If the chain is legitimate but long, pre-resolve redirects once and cache the final URL rather than re-chaining per request.
Example fix
// before proxy.URL = "http://example.com/file" // 12-hop redirect chain // after proxy.URL = "https://cdn.example.com/path/file" // final resolved URL
Defensive patterns
Strategy: retry
Validate before calling
// Caller side: pre-resolve the final URL to minimize redirect hops.
async function resolveFinalURL(u: string): Promise<string> {
const r = await fetch(u, { method: 'HEAD', redirect: 'follow' })
return r.url
} Try / catch
try { await proxyFetch(url) }
catch (e) {
if (/stopped after 10 redirects/i.test(String(e))) {
const final = await resolveFinalURL(url)
return proxyFetch(final)
}
throw e
} Prevention
- Configure plugins with the final resolved URL, not a redirecting alias.
- Watch for auth/CDN redirect loops on the target host.
- Cache the resolved final URL instead of re-chaining per request.
When it happens
Trigger: A plugin's proxy handler (writeProxyResponse) fetches a URL whose server responds with a redirect chain longer than 10 hops; or a self-referential loop between hosts.
Common situations: Target URL moved and chains through many hops; CDN/auth gateway redirect loop; misconfigured target returning Location to itself; plugin proxies an URL that requires login and bounces through auth redirects.
Related errors
- ${data.error.message}
- Failed to save agent session
- Failed to remove agent session
- Failed to update agent session permission
- Agent capability name and description are required
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/43ab3ea22a4436c8.
Report an issue: GitHub.