risingwavelabs/risingwave · error · SinkError

Can't clone request

Error message

Can't clone request

What it means

`send_stream_load_request` manually follows FE redirects (to preserve Authorization headers that reqwest would strip) by cloning the outgoing request each round with `try_clone`. `Request::try_clone` returns None when the request body is not cloneable (a non-in-memory/streaming body), so cloning fails and this error is thrown.

Source

Thrown at src/connector/src/sink/doris_starrocks_connector.rs:418

/// The reason we handle the redirection manually is that if we let `reqwest` handle the redirection
/// automatically, it will remove sensitive headers (such as Authorization) during the redirection,
/// and there's no way to prevent this behavior.
/// Please note, the FE address that user specified might be a FE follower not the leader, in this case,
/// the follower FE will redirect request to leader FE and then to BE.
async fn send_stream_load_request(
    client: Client,
    mut request: Request,
    fe_host: &str,
) -> Result<StreamLoadResponse> {
    // possible redirection paths:
    // RW <-> follower FE -> leader FE -> BE
    // RW <-> leader FE -> BE
    // RW <-> leader FE
    for _ in 0..2 {
        let original_http_port = request.url().port();
        let mut request_for_redirection = request
            .try_clone()
            .ok_or_else(|| SinkError::DorisStarrocksConnect(anyhow!("Can't clone request")))?;
        let resp = client.execute(request).await.map_err(|err| {
            SinkError::DorisStarrocksConnect(
                anyhow!(err).context("sending stream load request failed"),
            )
        })?;
        let be_url = try_get_be_url(&resp, fe_host)?;
        match be_url {
            Some(be_url) => {
                // we used an unconventional method to detect if we are currently redirecting to FE leader, i.e.,
                // by comparing the port of the redirected url with that of the original request, if they are same, we consider
                // this is a FE address. Because in practice, no one would deploy their `StarRocks` cluster with the same
                // http port for both FE and BE. However, this is a potentially problematic assumption,
                // we may investigate a better way to do this. For example, we could use the `show backends` command to check
                // if the host of the redirected url is in the list. However, `show backends` requires
                // the system-level privilege, which could break the backward compatibility.
                let redirected_port = be_url.port();
                *request_for_redirection.url_mut() = be_url;
                if redirected_port == original_http_port {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the request passed to `send_stream_load_request` has an in-memory body (`Bytes`/`&'static [u8]`), which reqwest can clone
  2. Refactor so the body is rebuilt from a buffer instead of relying on `try_clone` on a streaming body
  3. Check recent changes to request construction in `send`/`build_txn_inserter`; this is typically a code bug, not a user config issue
  4. As a workaround, reconstruct the request per redirect round using stored URL/headers/body instead of cloning

Example fix

// before: body built as a stream, try_clone returns None
let request = client.put(url).body(Body::wrap_stream(stream));
// after: body buffered so the request is clonable
let request = client.put(url).body(bytes); // Bytes: try_clone succeeds
Defensive patterns

Strategy: validation

Validate before calling

// ensure the request body is clonable before calling the send path
assert!(matches!(body, reqwest::Body::Bytes(_) | reqwest::Body::Wrapper(_)) == false || !matches!(body, reqwest::Body::Stream(_)), "body must be buffered, not a stream");

Try / catch

let request = match request.try_clone() {
    Some(cloned) => cloned,
    None => return Err(rebuild_request_from_parts(&request)),
};

Prevention

When it happens

Trigger: `send_stream_load_request` is called (from `send` or `build_txn_inserter`) with a request whose body cannot be cloned — e.g. a streaming body rather than a fully-buffered `Bytes` body — on the first or redirect iteration.

Common situations: Upstream code change replaced a `Bytes` body with a stream; regression after refactor of how the txn/stream-load request body is constructed; attempting to reuse a request that already had its body consumed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/4fd2b33d34962784. Report an issue: GitHub.