{"record":{"id":"bb7547af87f1e943","repo":"XTLS/Xray-core","slug":"stopped-after-10-redirects","errorCode":null,"errorMessage":"stopped after 10 redirects","messagePattern":"stopped after 10 redirects","errorType":"console","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"app/geodata/download.go","lineNumber":115,"sourceCode":"\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\thost, _, _ := net.SplitHostPort(address)\n\t\t\t\t\ttlsConn := utls.UClient(conn, &utls.Config{ServerName: host}, utls.HelloChrome_Auto)\n\t\t\t\t\thandshakeCtx, cancel := context.WithTimeout(ctx, idleTimeout)\n\t\t\t\t\tdefer cancel()\n\t\t\t\t\tif err := tlsConn.HandshakeContext(handshakeCtx); err != nil {\n\t\t\t\t\t\tconn.Close()\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn tlsConn, nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\t\tif req.URL.Scheme != \"https\" {\n\t\t\t\t\treturn errors.New(\"redirected to non-https URL: \", req.URL.String())\n\t\t\t\t}\n\t\t\t\tif len(via) >= 10 {\n\t\t\t\t\treturn errors.New(\"stopped after 10 redirects\")\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t} else {\n\t\treturn &http.Client{\n\t\t\tTransport: &http.Transport{\n\t\t\t\tProxy:                 nil,\n\t\t\t\tDisableKeepAlives:     true,\n\t\t\t\tDialContext:           dial,\n\t\t\t\tResponseHeaderTimeout: idleTimeout,\n\t\t\t},\n\t\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\t\tif req.URL.Scheme != \"https\" {\n\t\t\t\t\treturn errors.New(\"redirected to non-https URL: \", req.URL.String())\n\t\t\t\t}\n\t\t\t\tif len(via) >= 10 {\n\t\t\t\t\treturn errors.New(\"stopped after 10 redirects\")","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/geodata/download.go#L97-L133","documentation":"CheckRedirect hook on the HTTPS geodata client: net/http by default allows up to 10 redirects; this hook counts them via len(via) >= 10 and stops the chain with this error to prevent redirect loops.","triggerScenarios":"The geodata asset URL enters a redirect cycle (A -> B -> A, or a self-redirecting mirror) exceeding 10 hops.","commonSituations":"Misconfigured CDN or mirror with a redirect loop; a proxy/MITM rewriting each hop; cookie-less clients bounced between regional mirrors.","solutions":["Pin the final https URL directly in the geodata asset config so no redirects are needed","Diagnose the loop: curl -sIL <asset-url> and count/inspect Location headers","Switch to a stable mirror (raw.githubusercontent.com or jsdelivr)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := d.download(assets)\nif err != nil && strings.Contains(err.Error(), \"stopped after 10 redirects\") {\n    // replace asset URL with the direct final URL instead of retrying blindly\n}","preventionTips":["Pin direct final URLs in geodata assets to avoid long redirect chains","Audit mirrors with curl -IL when first configuring them"],"tags":["network","geodata","redirect","download"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}