ekzhang/bore · error

unexpected initial non-hello message

Error message

unexpected initial non-hello message

What it means

In `Client::new`, the first server message after the handshake was neither `Hello`, `Error`, nor `Challenge` — an unexpected message variant arrived where the protocol requires `Hello(remote_port)`. This indicates the remote endpoint is not speaking the expected bore protocol or the stream is desynchronized.

Solutions

  1. Verify the target host:port actually runs a bore server (`bore server`).
  2. Check for proxies/load balancers that might answer before the bore server does.
  3. Ensure client and server use compatible bore versions.
  4. Confirm you are not connecting to the HTTP control port of another service.

Example fix

// before (wrong port: HTTP service)
bore local 3000 --to example.com --port 80
// after (port where bore server listens)
bore local 3000 --to example.com --port 2200
Defensive patterns

Strategy: validation

Validate before calling

// Sanity-check the endpoint before connecting
assert!(!to_host.contains("http"), "use a TCP host:port, not a URL");
// Verify a bore server is listening on that port before Client::new

Try / catch

match Client::new(...).await {
    Err(e) if e.to_string().contains("unexpected initial non-hello message") => {
        eprintln!("target is not a compatible bore server; check host/port/version");
        std::process::exit(2);
    }
    other => other,
}

Prevention

When it happens

Trigger: Connecting to a port not actually served by a compatible bore server; a proxy returning an HTTP banner or other protocol data; a mismatched protocol version re-framing messages incorrectly.

Common situations: Wrong port forwarded to a non-bore service; a reverse proxy or load balancer intercepting the TCP connection; client and server versions with incompatible message enums; connecting to an HTTP endpoint by mistake.

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 ekzhang/bore@00a735a899 (2026-09-08). Data as JSON: /api/errors/58ea5bee824e62d4. Report an issue: GitHub.

Appendix: source

Thrown at src/client.rs:56

        local_port: u16,
        to: &str,
        port: u16,
        secret: Option<&str>,
    ) -> Result<Self> {
        let mut stream = Delimited::new(connect_with_timeout(to, CONTROL_PORT).await?);
        let auth = secret.map(Authenticator::new);
        if let Some(auth) = &auth {
            auth.client_handshake(&mut stream).await?;
        }

        stream.send(ClientMessage::Hello(port)).await?;
        let remote_port = match stream.recv_timeout().await? {
            Some(ServerMessage::Hello(remote_port)) => remote_port,
            Some(ServerMessage::Error(message)) => bail!("server error: {message}"),
            Some(ServerMessage::Challenge(_)) => {
                bail!("server requires authentication, but no client secret was provided");
            }
            Some(_) => bail!("unexpected initial non-hello message"),
            None => bail!("unexpected EOF"),
        };
        info!(remote_port, "connected to server");
        info!("listening at {to}:{remote_port}");

        Ok(Client {
            conn: Some(stream),
            to: to.to_string(),
            local_host: local_host.to_string(),
            local_port,
            remote_port,
            auth,
        })
    }

    /// Returns the port publicly available on the remote.
    pub fn remote_port(&self) -> u16 {
        self.remote_port

View on GitHub (pinned to 00a735a899)