risingwavelabs/risingwave · error · SinkError

Can't get fe host from url

Error message

Can't get fe host from url

What it means

InserterInnerBuilder::new parses the FE URL provided in the sink options and extracts its host with host_str(). If the URL parses but contains no host (e.g. a scheme-less or path-only string), this error is raised because all stream-load requests are built from the FE host.

Source

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

    url: String,
    header: HashMap<String, String>,
    #[expect(dead_code)]
    sender: Option<Sender>,
    fe_host: String,
    stream_load_http_timeout: Duration,
}
impl InserterInnerBuilder {
    pub fn new(
        url: String,
        db: String,
        table: String,
        header: HashMap<String, String>,
        stream_load_http_timeout_ms: u64,
    ) -> Result<Self> {
        let fe_host = Url::parse(&url)
            .map_err(|err| SinkError::DorisStarrocksConnect(anyhow!(err)))?
            .host_str()
            .ok_or_else(|| SinkError::DorisStarrocksConnect(anyhow!("Can't get fe host from url")))?
            .to_owned();
        let url = format!("{}/api/{}/{}/_stream_load", url, db, table);
        let stream_load_http_timeout = Duration::from_millis(stream_load_http_timeout_ms);

        Ok(Self {
            url,
            sender: None,
            header,
            fe_host,
            stream_load_http_timeout,
        })
    }

    fn build_request(&self, uri: String) -> Result<RequestBuilder> {
        let client = Client::builder()
            .pool_idle_timeout(POOL_IDLE_TIMEOUT)
            .redirect(redirect::Policy::none()) // we handle redirect by ourselves
            .build()

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Set the `url` option to a full URL with scheme and host, e.g. `http://fe-host:8030`.
  2. Verify no empty/placeholder values in the WITH clause (unresolved template variables).
  3. Wrap IPv6 literals in brackets: `http://[::1]:8030`.
  4. Test the URL with a quick curl to the FE port before creating the sink.

Example fix

// before
WITH ('connector'='doris', 'url'='fe-host:8030', ...)
// after
WITH ('connector'='doris', 'url'='http://fe-host:8030', ...)
Defensive patterns

Strategy: validation

Validate before calling

fn validate_fe_url(u: &str) -> Result<(), String> {
    match url::Url::parse(u) {
        Ok(p) if p.host_str().is_some() => Ok(()),
        _ => Err(format!("doris `url` must include scheme and host, got: {u}")),
    }
}

Type guard

fn fe_host(u: &str) -> Option<&str> {
    url::Url::parse(u).ok().and_then(|p| p.host_str())
}

Try / catch

match build_inserter(url, db, table, header, timeout).await {
    Err(SinkError::DorisStarrocksConnect(e)) if e.to_string().contains("Can't get fe host from url") => {
        eprintln!("fix the doris `url` option: {e}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Configuring the Doris sink `url` option as something Url::parse accepts but that has no host component, such as 'http:///api' or a relative path, during sink build.

Common situations: Typos dropping the hostname; forgetting the scheme so the URL parser treats text differently; templated config values left unfilled; IPv6 hosts without brackets.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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