tokio-rs/tokio · error · io::Error

could not resolve to any address

Error message

could not resolve to any address

What it means

UdpSocket::bind follows the same pattern as the TCP variants: resolve, iterate, fall back to this InvalidInput error only when last_err is None. That branch is reached exclusively when to_socket_addrs returned an empty iterator — no addresses to attempt a bind against.

Source

Thrown at tokio/src/net/udp.rs:162

    ///     let sock = UdpSocket::bind("0.0.0.0:8080").await?;
    ///     // use `sock`
    /// #   let _ = sock;
    ///     Ok(())
    /// }
    /// ```
    pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
        let addrs = to_socket_addrs(addr).await?;
        let mut last_err = None;

        for addr in addrs {
            match UdpSocket::bind_addr(addr) {
                Ok(socket) => return Ok(socket),
                Err(e) => last_err = Some(e),
            }
        }

        Err(last_err.unwrap_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "could not resolve to any address",
            )
        }))
    }

    fn bind_addr(addr: SocketAddr) -> io::Result<UdpSocket> {
        let sys = mio::net::UdpSocket::bind(addr)?;
        UdpSocket::new(sys)
    }

    #[track_caller]
    fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> {
        let io = PollEvented::new(socket)?;
        Ok(UdpSocket { io })
    }

    /// Creates new `UdpSocket` from a previously bound `std::net::UdpSocket`.

View on GitHub (pinned to 625954f365)

Solutions

  1. Bind to a literal IP (e.g. "0.0.0.0:port" or "127.0.0.1:port") — the recommended idiom for UDP listeners.
  2. Pre-resolve with tokio::net::lookup_host and assert non-empty before binding.
  3. Inspect the error: InvalidInput with no raw_os_error means empty resolution; otherwise a real bind error.
  4. Validate the hostname with an external resolver (dig/nslookup).

Example fix

// before
let s = UdpSocket::bind("my-svc:9090").await?;

// after
let s = UdpSocket::bind(("0.0.0.0", 9090)).await?;
// or check resolution explicitly:
if tokio::net::lookup_host("my-svc:9090").await?.next().is_none() {
    return Err(anyhow::anyhow!("no addresses for my-svc"));
}
Defensive patterns

Strategy: validation

Validate before calling

let addrs: Vec<_> = tokio::net::lookup_host((host.as_str(), port)).await?.collect();
if addrs.is_empty() {
    return Err(anyhow::anyhow!("{host} resolved to no addresses"));
}
let s = UdpSocket::bind(addrs[0]).await?;

Type guard

fn is_empty_resolution(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::InvalidInput && e.raw_os_error().is_none()
}

Try / catch

match UdpSocket::bind(addr).await {
    Ok(s) => Ok(s),
    Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.raw_os_error().is_none() => {
        Err(anyhow::anyhow!("no addresses resolved for {addr}"))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling UdpSocket::bind("host:port") where the hostname resolves to zero A/AAAA records, so the for loop body never executes and last_err stays None.

Common situations: Bad hostname; service with no A record; misconfigured DNS or /etc/hosts; binding by name to a host that no longer exists; passing a name where a literal IP is intended.

Related errors


AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11). Data as JSON: /api/errors/291544047b24f9ec. Report an issue: GitHub.