{"record":{"id":"26fad9357c12416b","repo":"risingwavelabs/risingwave","slug":"redirection-occur-more-than-twice-when-sending-str","errorCode":null,"errorMessage":"redirection occur more than twice when sending stream load request","messagePattern":"redirection occur more than twice when sending stream load request","errorType":"exception","errorClass":"SinkError","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/doris_starrocks_connector.rs","lineNumber":447,"sourceCode":"                // this is a FE address. Because in practice, no one would deploy their `StarRocks` cluster with the same\n                // http port for both FE and BE. However, this is a potentially problematic assumption,\n                // we may investigate a better way to do this. For example, we could use the `show backends` command to check\n                // if the host of the redirected url is in the list. However, `show backends` requires\n                // the system-level privilege, which could break the backward compatibility.\n                let redirected_port = be_url.port();\n                *request_for_redirection.url_mut() = be_url;\n                if redirected_port == original_http_port {\n                    // redirected to FE, continue another round.\n                    request = request_for_redirection;\n                } else {\n                    // we got BE address here\n                    return Ok(StreamLoadResponse::BeRequest(request_for_redirection));\n                }\n            }\n            None => return Ok(StreamLoadResponse::HttpResponse(resp)),\n        }\n    }\n    Err(SinkError::DorisStarrocksConnect(anyhow!(\n        \"redirection occur more than twice when sending stream load request\"\n    )))\n}\n\npub struct MetaRequestSender {\n    client: Client,\n    request: Request,\n    fe_host: String,\n}\n\nimpl MetaRequestSender {\n    pub fn new(client: Client, request: Request, fe_host: String) -> Self {\n        Self {\n            client,\n            request,\n            fe_host,\n        }\n    }","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/doris_starrocks_connector.rs#L429-L465","documentation":"send_stream_load_request manually follows FE redirects (to preserve Authorization headers that reqwest would strip) but allows at most 2 rounds: follower FE -> leader FE -> BE. If after two redirected requests the response still redirects (and the redirected URL's port equals the original, so it looks like another FE), this error is thrown.","triggerScenarios":"The stream load / transaction endpoint keeps returning a redirect to an FE (Location URL with the same HTTP port as the request) after two hops — e.g. a redirect loop among FE nodes, or a misconfigured proxy that always redirects with the same port, so the FE-vs-BE port heuristic never terminates.","commonSituations":"Deployments where FE and BE share the same HTTP port (the code's heuristic then mistakes a BE redirect for an FE redirect); a chain of more than one follower FE; a reverse proxy or load balancer in front of FE that re-redirects endlessly; FE leader election flapping.","solutions":["Ensure FE and BE use different HTTP ports (avoid the same http_port for FE and BE) so the FE/BE redirect detection heuristic works","Check for a redirect loop: curl -v --location-trusted against the stream load URL and inspect the Location chain","Remove or correctly configure any reverse proxy in front of the FE so it does not repeatedly redirect with the same port","Verify the configured URL points to an FE (not a BE) and that only one follower hop is expected; point directly at the leader FE if possible"],"exampleFix":"# before: FE and BE both on 8030 -> redirect heuristic misfires, loops twice\n# after: give BE a distinct http_port (e.g. 8040) so BE redirects are detected and the loop ends","handlingStrategy":"validation","validationCode":"// Before creating the sink, verify the redirect chain terminates:\nlet client = reqwest::Client::builder().redirect(reqwest::redirect::Policy::none()).build()?;\nlet mut url = format!(\"{endpoint}/api/transaction/begin\");\nfor hop in 0..3 {\n    let resp = client.post(&url).send().await?;\n    match resp.headers().get(reqwest::header::LOCATION) {\n        Some(loc) => {\n            url = resp.url().join(loc.to_str()?)?.to_string();\n            if hop == 2 { return Err(\"redirect chain exceeds 2 hops; FE/BE port heuristic will fail\".into()); }\n        }\n        None => break,\n    }\n}","typeGuard":"fn looks_like_fe_redirect(location: &reqwest::Url, original_port: Option<u16>) -> bool {\n    location.port() == original_port // same heuristic the connector uses: same port => FE\n}","tryCatchPattern":"match send_stream_load_request(client, request, &fe_host).await {\n    Err(e) if e.to_string().contains(\"redirection occur more than twice\") => {\n        // fall back to querying the leader FE directly (SHOW FRONTENDS) and retry once against it\n    }\n    other => other?,\n}","preventionTips":["Deploy FE and BE with distinct http_port values so the port-based FE/BE detection works","Avoid placing redirecting reverse proxies in front of the FE; route directly to the FE","Point the sink URL at a stable FE address (VIP/DNS that resolves to the leader) to minimize follower redirect hops","After FE topology changes, re-verify only one follower->leader redirect is needed"],"tags":["http","redirect","doris","starrocks","sink"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}