linera-io/linera-protocol · error

Invalid address to connect to

Error message

Invalid address to connect to

What it means

TransportProtocol::connect (linera-rpc/src/simple/transport.rs:150) resolves the given address with tokio's lookup_host and expects success before connecting. lookup_host fails when the address string is malformed (no port, non-numeric port, invalid characters) or DNS resolution fails (unknown host, resolver unreachable). Although connect returns io::Result, this resolution step panics instead of returning the error, so a bad simple-network address crashes the caller (used by new, open_in_memory, subscribe_to_shards, try_proxy_message, and the net proxy main).

Source

Thrown at linera-rpc/src/simple/transport.rs:156

pub trait Transport:
    Stream<Item = Result<RpcMessage, codec::Error>> + Sink<RpcMessage, Error = codec::Error>
{
}

impl<T> Transport for T where
    T: Stream<Item = Result<RpcMessage, codec::Error>> + Sink<RpcMessage, Error = codec::Error>
{
}

impl TransportProtocol {
    /// Creates a transport for this protocol.
    pub async fn connect(
        self,
        address: impl ToSocketAddrs,
    ) -> Result<impl Transport, std::io::Error> {
        let mut addresses = lookup_host(address)
            .await
            .expect("Invalid address to connect to");
        let address = addresses
            .next()
            .expect("Couldn't resolve address to connect to");

        let stream: futures::future::Either<_, _> = match self {
            TransportProtocol::Udp => {
                let socket = UdpSocket::bind(&"0.0.0.0:0").await?;

                UdpFramed::new(socket, Codec)
                    .with(move |message| future::ready(Ok((message, address))))
                    .map_ok(|(message, _address)| message)
                    .left_stream()
            }
            TransportProtocol::Tcp => {
                let stream = TcpStream::connect(address).await?;

                Framed::new(stream, Codec).right_stream()
            }

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Use an explicit 'host:port' string with a numeric port, e.g. 'localhost:9521' or '10.0.0.1:9521'.
  2. Prefer IP literals in DNS-less environments (containers, test clusters).
  3. Verify resolution outside the app: `getent hosts <host>` or `nslookup <host>`.
  4. If embedding, validate addresses up front with tokio::net::lookup_host before calling connect.

Example fix

// before
let transport = TransportProtocol::Tcp.connect("validator").await?; // no port -> panic

// after
let transport = TransportProtocol::Tcp.connect("validator:9521").await?;

// when embedding, validate first
if tokio::net::lookup_host((host.as_str(), port)).await.is_err() {
    return Err(anyhow::anyhow!("cannot resolve {host}:{port}"));
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate host:port resolution before calling connect:
async fn resolvable(addr: &str) -> bool {
    tokio::net::lookup_host(addr.to_string()).await.map(|mut s| s.next().is_some()).unwrap_or(false)
}
if !resolvable(&peer_address).await {
    anyhow::bail!("address '{peer_address}' is malformed or unresolvable; use host:port with a numeric port");
}

Type guard

fn is_host_port(s: &str) -> bool {
    s.parse::<std::net::SocketAddr>().is_ok()
        || s.rsplit_once(':').map(|(h, p)| !h.is_empty() && p.parse::<u16>().is_ok()).unwrap_or(false)
}

Try / catch

// The library panics during resolution despite returning io::Result; wrap with catch_unwind when embedding:
let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| {
    block_on(TransportProtocol::Tcp.connect(address.clone()))
}));
match outcome {
    Ok(Ok(transport)) => Ok(transport),
    Ok(Err(e)) => Err(e.into()),
    Err(_) => Err(anyhow::anyhow!("invalid or unresolvable address: {address:?}")),
}

Prevention

When it happens

Trigger: Passing an address without a port ('localhost'), a non-numeric port ('localhost:http'), or an unresolvable hostname to a simple (TCP/UDP) network client - e.g. a --listen-on/peer address from the proxy or shard configuration, or an address typed into net-proxy invocations.

Common situations: Configuring the simple network layer with bare hostnames when DNS is unavailable in the container; typos in host:port strings; expecting service-name resolution ('validator:9521') in an environment without a working DNS; splitting a config value that leaves an empty string.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/25090f065abe3aa8. Report an issue: GitHub.