rathole-org/rathole · error

missing semicolon

Error message

missing semicolon

What it means

host_port_pair splits an `host:port` string at the last colon via rfind(':') and panics with "missing semicolon" if there is no colon. The panic means the address string passed to tcp_connect_with_proxy/connect lacked a port component.

Solutions

  1. Append the port to the address, e.g. `example.com:443`.
  2. Validate user-supplied addresses contain a `host:port` before passing them in.
  3. For IPv6 literals use bracketed form `[::1]:443` so the last-colon split is unambiguous.
  4. Consider using a parsing helper (SocketAddr::from_str) to get a proper error instead of a panic.

Example fix

// before
let addr = "example.com"; // panics in host_port_pair
// after
let addr = "example.com:443";
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_host_port(s: &str) -> Result<(), String> {
    match s.rfind(':') {
        Some(i) if i + 1 < s.len() && s[i+1..].chars().all(|c| c.is_ascii_digit()) => Ok(()),
        _ => Err(format!("address '{}' must be in host:port form", s)),
    }
}

Try / catch

std::panic::catch_unwind(|| host_port_pair(addr))
    .and_then(|r| r)
    .map_err(|_| anyhow!("address '{}' must include a port (host:port)", addr));

Prevention

When it happens

Trigger: Calling host_port_pair (via connect or tcp_connect_with_proxy) with an address string like "example.com" or "example.com." without `:port`.

Common situations: Users writing just a hostname in the config's remote_addr; shell/env interpolation dropping the `:port` suffix; copy-paste of a URL where the port got separated; IPv6 literals without brackets plus port, causing a malformed string.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at src/helper.rs:62

}

#[allow(dead_code)]
pub fn feature_neither_compile(feature1: &str, feature2: &str) -> ! {
    panic!(
        "Neither of the feature '{}' or '{}' is compiled in this binary. Please re-compile rathole",
        feature1, feature2
    )
}

pub async fn to_socket_addr<A: ToSocketAddrs>(addr: A) -> Result<SocketAddr> {
    lookup_host(addr)
        .await?
        .next()
        .ok_or_else(|| anyhow!("Failed to lookup the host"))
}

pub fn host_port_pair(s: &str) -> Result<(&str, u16)> {
    let semi = s.rfind(':').expect("missing semicolon");
    Ok((&s[..semi], s[semi + 1..].parse()?))
}

/// Create a UDP socket and connect to `addr`
pub async fn udp_connect<A: ToSocketAddrs>(addr: A, prefer_ipv6: bool) -> Result<UdpSocket> {

    let (socket_addr, bind_addr);

    match prefer_ipv6 {
        false => {
            socket_addr = to_socket_addr(addr).await?;

            bind_addr = match socket_addr {
                SocketAddr::V4(_) => "0.0.0.0:0",
                SocketAddr::V6(_) => ":::0",
            };
        },
        true => {

View on GitHub (pinned to a292f7ed54)