tokio-rs/tokio · error · io::Error
could not resolve to any address
Error message
could not resolve to any address
What it means
TcpListener::bind resolves the address via to_socket_addrs, iterates each candidate, and on the failure path calls unwrap_or_else to build this InvalidInput error only when last_err is None — i.e. the resolved iterator was empty. If at least one address was tried, the OS-level bind error is returned instead. So this message specifically means 'DNS returned zero usable addresses.'
Source
Thrown at tokio/src/net/tcp/listener.rs:115
///
/// # let _ = listener;
/// Ok(())
/// }
/// ```
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
let addrs = to_socket_addrs(addr).await?;
let mut last_err = None;
for addr in addrs {
match TcpListener::bind_addr(addr) {
Ok(listener) => return Ok(listener),
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<TcpListener> {
let listener = mio::net::TcpListener::bind(addr)?;
TcpListener::new(listener)
}
}
/// Accepts a new incoming connection from this listener.
///
/// This function will yield once a new TCP connection is established. When
/// established, the corresponding [`TcpStream`] and the remote peer's
/// address will be returned.
///View on GitHub (pinned to 625954f365)
Solutions
- Pre-resolve with tokio::net::lookup_host and inspect the iterator length before binding.
- Use a literal IP (127.0.0.1:port or [::]:port) instead of a hostname for listener binds — the common idiom.
- Verify DNS records externally (dig/nslookup) for the hostname.
- Distinguish this case from a real bind failure by checking that error.kind() == InvalidInput AND no os error code is attached.
Example fix
// before
let l = TcpListener::bind("svc.example:8080").await?;
// after
let l = TcpListener::bind(("0.0.0.0", 8080)).await?;
// or pre-resolve:
let mut addrs = tokio::net::lookup_host("svc.example:8080").await?;
if addrs.next().is_none() {
return Err(anyhow::anyhow!("no addresses resolved"));
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-resolve and require a non-empty address list:
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 listener = TcpListener::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 TcpListener::bind(addr).await {
Ok(l) => Ok(l),
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
- Bind listeners to literal IPs (0.0.0.0 / 127.0.0.1) instead of hostnames.
- Cache resolved addresses and log when resolution yields zero results.
- Distinguish empty resolution (no raw os error) from kernel bind failures.
- Validate hostnames at config load time, not at bind time.
When it happens
Trigger: Calling TcpListener::bind(addr) where addr is a hostname:port string whose resolution yields no A/AAAA records, or an empty address list. The for loop body never executes, last_err stays None, and the fallback error fires.
Common situations: Misspelled hostname; a service that has no DNS record (bare SRV/CNAME with no A); /etc/hosts misconfiguration; resolver returning an empty answer; passing an empty-string host that resolves to nothing on the platform.
Related errors
- could not resolve to any address
- could not resolve to any address
- invalid address family (not IPv4 or IPv6)
- no addresses to send data to
- sender not available
AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11).
Data as JSON: /api/errors/0db36d41bddbe7a4.
Report an issue: GitHub.