rathole-org/rathole · error

Unexpected type of hello

Error message

Unexpected type of hello

What it means

After connecting to the server, the client reads a hello message and expects ControlChannelHello; any other hello variant triggers this bail. It means the remote peer answered on the control channel with unexpected data — typically not a rathole server at all, or a version/protocol mismatch (though version mismatch has its own error).

Solutions

  1. Verify remote_addr points to the rathole server's control-channel port, not another service
  2. Confirm the server is actually running and reachable at that address
  3. Check for port conflicts — something else may be listening on the configured port
  4. Test with a raw connection (nc host port) to see what the endpoint responds with

Example fix

// before (client.toml)
[client]
remote_addr = "example.com:80"  # wrong port, HTTP server answers

// after
[client]
remote_addr = "example.com:2333"  # rathole server's control port
Defensive patterns

Strategy: validation

Validate before calling

// Probe the endpoint before pointing rathole at it:
nc -vz example.com 2333 && timeout 3 bash -c 'exec 3<>/dev/tcp/example.com/2333; head -c 64 <&3' | xxd | head
// Non-rathole banner bytes mean wrong host/port.

Prevention

When it happens

Trigger: read_hello returns a Hello variant that is not ControlChannelHello inside ControlChannel::run, immediately after TCP connect to remote_addr.

Common situations: remote_addr points at the wrong host or port (e.g. the HTTP port, an SSH daemon, or the rathole data port); a proxy or firewall returns a non-rathole banner; connecting a client to another client.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/client.rs:429

            .connect(&remote_addr)
            .await
            .with_context(|| format!("Failed to connect to {}", &self.remote_addr))?;
        T::hint(&conn, SocketOpts::for_control_channel());

        // Send hello
        debug!("Sending hello");
        let hello_send =
            Hello::ControlChannelHello(CURRENT_PROTO_VERSION, self.digest[..].try_into().unwrap());
        conn.write_all(&bincode::serialize(&hello_send).unwrap())
            .await?;
        conn.flush().await?;

        // Read hello
        debug!("Reading hello");
        let nonce = match read_hello(&mut conn).await? {
            ControlChannelHello(_, d) => d,
            _ => {
                bail!("Unexpected type of hello");
            }
        };

        // Send auth
        debug!("Sending auth");
        let mut concat = Vec::from(self.service.token.as_ref().unwrap().as_bytes());
        concat.extend_from_slice(&nonce);

        let session_key = protocol::digest(&concat);
        let auth = Auth(session_key);
        conn.write_all(&bincode::serialize(&auth).unwrap()).await?;
        conn.flush().await?;

        // Read ack
        debug!("Reading ack");
        match read_ack(&mut conn).await? {
            Ack::Ok => {}
            v => {

View on GitHub (pinned to a292f7ed54)