t8y2/dbx · error

invalid TDengine host

Error message

invalid TDengine host

What it means

Thrown in `build_from_fields` when the trimmed host value cannot be installed into the URL: if it parses as an IP address, `set_ip_host` failed; otherwise `set_host(Some(host))` rejected it as not a valid host per RFC 3986. Defaults (DEFAULT_HOST) are used only for empty input, so a non-empty but malformed host reaches Url and fails there.

Source

Thrown at agents/drivers/tdengine/src/config.rs:55

        set_query_param(&mut url, "tls_ca", params.ca_cert_path.trim());
    }
    let database = url
        .path_segments()
        .and_then(|mut segments| segments.find(|segment| !segment.is_empty()))
        .map(|segment| percent_decode_str(segment).decode_utf8_lossy().into_owned())
        .unwrap_or_default();
    Ok(BuiltDsn { value: url.into(), database })
}

fn build_from_fields(params: &ConnectParams) -> Result<Url> {
    let scheme = if params.ssl { "wss" } else { "ws" };
    let host = if params.host.trim().is_empty() { DEFAULT_HOST } else { params.host.trim() };
    let port = if params.port == 0 { DEFAULT_PORT } else { params.port };
    let username = if params.username.is_empty() { DEFAULT_USER } else { &params.username };
    let password = if params.password.is_empty() { DEFAULT_PASSWORD } else { &params.password };
    let mut url = Url::parse(&format!("{scheme}://{DEFAULT_HOST}:{port}/"))?;
    if let Ok(address) = host.parse::<IpAddr>() {
        url.set_ip_host(address).map_err(|_| anyhow::anyhow!("invalid TDengine host"))?;
    } else {
        url.set_host(Some(host)).map_err(|_| anyhow::anyhow!("invalid TDengine host"))?;
    }
    url.set_username(username).map_err(|_| anyhow::anyhow!("invalid TDengine username"))?;
    url.set_password(Some(password)).map_err(|_| anyhow::anyhow!("invalid TDengine password"))?;
    if !params.database.trim().is_empty() {
        url.set_path(&format!("/{}", params.database.trim()));
    }
    Ok(url)
}

fn apply_connection_fields(url: &mut Url, params: &ConnectParams) -> Result<()> {
    if url.username().is_empty() {
        let username = if params.username.is_empty() { DEFAULT_USER } else { &params.username };
        url.set_username(username).map_err(|_| anyhow::anyhow!("invalid TDengine username"))?;
    }
    if url.password().is_none() {
        let password = if params.password.is_empty() { DEFAULT_PASSWORD } else { &params.password };

View on GitHub (pinned to c0390bff16)

Solutions

  1. Set host to a bare hostname or IP only: no scheme, no port, no path (use the port field for the port).
  2. Strip scheme prefixes and paths: convert "http://host:6041" to host="host", port=6041.
  3. For IPv6, pass the address so set_ip_host accepts it (or use the standard bracketed form the parser expects).
  4. Trim whitespace/quotes from the host env var or config value before constructing params.

Example fix

// before
let params = ConnectionParams { host: "http://tdengine:6041".into(), .. };
// after
let params = ConnectionParams { host: "tdengine".into(), port: 6041, .. };
Defensive patterns

Strategy: validation

Validate before calling

fn validate_host(host: &str) -> Result<(), String> {
    let h = host.trim();
    if h.is_empty() {
        return Ok(()); // falls back to DEFAULT_HOST
    }
    if h.contains("://") || h.contains('/') || h.contains('@') || h.contains(' ') {
        return Err(format!("host must be a bare hostname or IP, got: {h}"));
    }
    if h.parse::<std::net::IpAddr>().is_ok() || h.parse::<std::net::ToSocketAddrs>().is_ok() {
        Ok(())
    } else {
        Err(format!("invalid TDengine host: {h}"))
    }
}

Type guard

fn is_bare_host(s: &str) -> bool {
    let h = s.trim();
    !h.is_empty()
        && !h.contains("://")
        && !h.contains('/')
        && !h.contains('@')
        && (h.parse::<std::net::IpAddr>().is_ok()
            || h.split('.').count() > 1
            || h == "localhost")
}

Prevention

When it happens

Trigger: Passing a host string containing spaces, underscores in invalid positions, a scheme/protocol prefix like "http://host", userinfo, a path ("host/db"), or an invalid IPv6 literal (not bracketed correctly); also an IP literal that url::set_ip_host rejects.

Common situations: Pasting "http://localhost:6041" into a host-only config field; copying "host:port" including the port into the host field; trailing slashes or paths in the host; environment variables with stray whitespace/quotes from shell interpolation; IPv6 addresses written without brackets.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/c795a415a0ac9584. Report an issue: GitHub.