{"record":{"id":"43ab3ea22a4436c8","repo":"siyuan-note/siyuan","slug":"stopped-after-10-redirects","errorCode":null,"errorMessage":"stopped after 10 redirects","messagePattern":"stopped after 10 redirects","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/server.go","lineNumber":237,"sourceCode":"\t\tif !shouldProxyHeader(key, connectionHeaders) {\n\t\t\tcontinue\n\t\t}\n\t\tdst.Del(key)\n\t\tfor _, value := range values {\n\t\t\tdst.Add(key, value)\n\t\t}\n\t}\n}\n\nfunc newProxyHTTPClient() *http.Client {\n\treturn &http.Client{\n\t\tTransport: &http.Transport{\n\t\t\tDialContext:        util.SSRFSafeDialer(30 * time.Second).DialContext,\n\t\t\tDisableCompression: true,\n\t\t},\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\tif len(via) >= 10 {\n\t\t\t\treturn fmt.Errorf(\"stopped after 10 redirects\")\n\t\t\t}\n\t\t\treq.Header.Del(\"Referer\")\n\t\t\treturn nil\n\t\t},\n\t\tTimeout: 0,\n\t}\n}\n\nfunc writeProxyResponse(c *gin.Context, proxy *ResponseProxy) {\n\tif proxy.URL == \"\" {\n\t\tc.String(http.StatusBadRequest, \"missing proxy url\")\n\t\treturn\n\t}\n\ttargetURL, err := url.ParseRequestURI(proxy.URL)\n\tif err != nil {\n\t\tc.String(http.StatusBadRequest, \"parse proxy url failed: %s\", err.Error())\n\t\treturn\n\t}","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/server.go#L219-L255","documentation":"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.","triggerScenarios":"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.","commonSituations":"Misconfigured target URL (http vs https ping-pong), auth-protected endpoints bouncing between login and original URL, or a looping short-link.","solutions":["Verify the target URL and fix the server-side redirect loop","Use the final https:// URL directly instead of one that redirects","Reduce redirect hops or contact the endpoint owner; the 10-hop limit is intentional"],"exampleFix":"// before\nfetch('http://example.com/data'); // redirects back and forth\n// after\nfetch('https://example.com/data'); // direct final URL","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"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; }","preventionTips":["Link to final https URLs instead of redirecting shorteners","Detect redirect loops on the target server side","Keep authentication cookies consistent so auth redirects do not cycle"],"tags":["http","redirect","network"],"backgroundTag":"request-timeout","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}