rathole-org/rathole · error

proxy url should have host field

Error message

proxy url should have host field

What it means

tcp_connect_with_proxy connects to the SOCKS5 proxy itself using url.host_str().expect(...). If the proxy Url lacks a host component, this panics at src/helper.rs:118. A proxy URL without a host cannot be dialed, so the invariant is enforced with an expect.

Solutions

  1. Provide a complete proxy URL including host, e.g. `socks5://proxy.example.com:1080`.
  2. Check the proxy environment variable / CLI flag value for typos or missing host.
  3. Validate the URL with Url::parse and assert host_str().is_some() before starting connections.
  4. If credentials are used, ensure the format `socks5://user:pass@host:port` is preserved.

Example fix

// before
let proxy = "socks5://:1080"; // no host -> panic
// after
let proxy = "socks5://127.0.0.1:1080";
Defensive patterns

Strategy: validation

Validate before calling

let url = Url::parse(proxy_str).map_err(|e| anyhow!("bad proxy url: {}", e))?;
if url.host_str().is_none() {
    anyhow::bail!("proxy url '{}' must include a host", proxy_str);
}

Type guard

fn valid_proxy_url(u: &Url) -> bool {
    u.host_str().is_some()
}

Prevention

When it happens

Trigger: Passing a proxy option whose URL has no host — e.g. `socks5://:1080`, a malformed URL, or an empty proxy string parsed to a host-less Url.

Common situations: Typo in the proxy URL (missing host between scheme and port); environment variable ALL_PROXY/HTTP_PROXY set to a malformed value; templated configs where the proxy host variable was empty.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of rathole-org/rathole@a292f7ed54 (2026-09-07). Data as JSON: /api/errors/1687e6d0c31c696e. Report an issue: GitHub.

Appendix: source

Thrown at src/helper.rs:118

            }
        }
    };
    let s = UdpSocket::bind(bind_addr).await?;
    s.connect(socket_addr).await?;
    s.connect(socket_addr).await?;
    Ok(s)
}

/// Create a TcpStream using a proxy
/// e.g. socks5://user:pass@127.0.0.1:1080 http://127.0.0.1:8080
pub async fn tcp_connect_with_proxy(
    addr: &AddrMaybeCached,
    proxy: Option<&Url>,
) -> Result<TcpStream> {
    if let Some(url) = proxy {
        let addr = &addr.addr;
        let mut s = TcpStream::connect((
            url.host_str().expect("proxy url should have host field"),
            url.port().expect("proxy url should have port field"),
        ))
        .await?;

        let auth = if !url.username().is_empty() || url.password().is_some() {
            Some(async_socks5::Auth {
                username: url.username().into(),
                password: url.password().unwrap_or("").into(),
            })
        } else {
            None
        };
        match url.scheme() {
            "socks5" => {
                async_socks5::connect(&mut s, host_port_pair(addr)?, auth).await?;
            }
            "http" => {
                let (host, port) = host_port_pair(addr)?;

View on GitHub (pinned to a292f7ed54)