seanmonstar/warp · critical
failed to bind to address
Error message
failed to bind to address
What it means
`warp::Server::bind(addr)` awaits `tokio::net::TcpListener::bind(addr)` and `.expect("failed to bind to address")`s the result (src/server.rs:78). If the OS refuses to bind (port already in use, permission denied, invalid address), the server panics at startup instead of returning an error. The docs explicitly recommend using `bind` on a listener + `incoming()` when you need to handle bind failures gracefully.
Solutions
- Bind the listener yourself and use `incoming()` so errors are values: `TcpListener::bind(addr).await?` then `warp::serve(routes).incoming(listener)`
- Check what occupies the port (`lsof -i :3030` / `ss -ltnp`) and stop it or pick a free port
- On Linux enable SO_REUSEADDR semantics via your own listener setup (socket2 crate) if TIME_WAIT reuse is the issue
- Use an unprivileged port (>=1024) or run with proper capabilities when binding low ports
Example fix
// before
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
// after
let listener = tokio::net::TcpListener::bind(([127, 0, 0, 1], 3030))
.await
.map_err(|e| eprintln!("failed to bind: {e}"))?;
warp::serve(routes).incoming(listener).await; Defensive patterns
Strategy: try-catch
Validate before calling
// before starting the server, check the port is free:
async fn port_free(addr: std::net::SocketAddr) -> bool {
tokio::net::TcpListener::bind(addr).await.is_ok()
} Try / catch
let listener = tokio::net::TcpListener::bind(addr)
.await
.map_err(|e| { eprintln!("bind {addr} failed: {e}"); e })?;
warp::serve(routes).incoming(listener).await; Prevention
- Prefer binding your own TcpListener + incoming() over run()/bind() so bind errors are recoverable
- Read the listen port from an env var (e.g. PORT) with a free fallback instead of hardcoding
- In dev, use a process manager or port-picking (bind port 0 and report the chosen port)
- Check for leftover processes/containers holding the port before restart scripts
When it happens
Trigger: Starting the server with `warp::serve(routes).run(([127,0,0,1],3030))`/`bind((addr, port))` when the port is already taken by another process, the port is privileged (<1024) without root, or the interface address doesn't exist on the host.
Common situations: Two dev servers running concurrently; a previous instance that didn't release the port (crashed process, lingering container); deploying to a platform where PORT is privileged or already bound; typo'd or broadcast/unassigned listen addresses in config.
Related errors
- polled after complete
- polled after complete
- polled after complete
- polled after complete
- polled after complete
AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09).
Data as JSON: /api/errors/f05a27ee68f15600.
Report an issue: GitHub.
Appendix: source
Thrown at src/server.rs:78
/// Panics if we are unable to bind to the provided address.
///
/// To handle bind failures, bind a listener and call `incoming()`.
pub async fn run(self, addr: impl Into<SocketAddr>) {
self.bind(addr).await.run().await;
}
/// Binds this server.
///
/// # Panics
///
/// Panics if we are unable to bind to the provided address.
///
/// To handle bind failures, bind a listener and call `incoming()`.
pub async fn bind(self, addr: impl Into<SocketAddr>) -> Server<F, tokio::net::TcpListener, R> {
let addr = addr.into();
let acceptor = tokio::net::TcpListener::bind(addr)
.await
.expect("failed to bind to address");
self.incoming(acceptor)
}
/// Configure the server with an acceptor of incoming connections.
pub fn incoming<A>(self, acceptor: A) -> Server<F, A, R> {
Server {
acceptor,
filter: self.filter,
pipeline: self.pipeline,
runner: self.runner,
}
}
// pub fn conn
}
impl<F, A, R> Server<F, A, R>View on GitHub (pinned to ff34d7213e)