block/buzz · error
Failed to bind {}: {e}
Error message
Failed to bind {}: {e} What it means
serve() binds the main WebSocket/HTTP listener on config.bind_addr, which comes from BUZZ_BIND_ADDR (default 0.0.0.0:3000, config.rs:462-464); this error wraps the tokio TcpListener::bind io::Error. Address syntax problems are rejected earlier at config load (ConfigError 'invalid BUZZ_BIND_ADDR'), so reaching this line means the address parsed fine but the OS refused the bind — usually AddrInUse, AddrNotAvailable (IP not present on any interface), or PermissionDenied on privileged ports.
Source
Thrown at crates/buzz-relay/src/main.rs:1332
// retains ownership of every delayed close until its 1012 frame has
// been flushed and acknowledged (or its send loop cancelled).
let closed = if drain_jitter_ms == 0 {
drain_conn_manager.drain_all()
} else {
drain_conn_manager.drain_all_jittered(drain_jitter_ms).await
};
info!(
connections = closed,
jitter_ms = drain_jitter_ms,
max_jitter_ms = MAX_DRAIN_JITTER_MS,
"Signalled restart close to all live WebSocket connections"
);
hard_shutdown_abort
});
let tcp_listener = tokio::net::TcpListener::bind(&config.bind_addr)
.await
.map_err(|e| anyhow::anyhow!("Failed to bind {}: {e}", config.bind_addr))?;
info!(addr = %config.bind_addr, "buzz-relay TCP listening");
#[cfg(unix)]
if let Some(ref uds_path) = config.uds_path {
use std::os::unix::fs::FileTypeExt as _;
match std::fs::symlink_metadata(uds_path) {
Ok(meta) if meta.file_type().is_socket() => {
let _ = std::fs::remove_file(uds_path);
}
Ok(_) => {
return Err(anyhow::anyhow!(
"BUZZ_UDS_PATH {uds_path} exists but is not a socket"
));
}
Err(_) => {}
}
let uds_listener = tokio::net::UnixListener::bind(uds_path)
.map_err(|e| anyhow::anyhow!("Failed to bind UDS {uds_path}: {e}"))?;View on GitHub (pinned to f956e6fe06)
Solutions
- Check the holder: ss -ltnp 'sport = :3000' and stop it, or change BUZZ_BIND_ADDR to a free port.
- If binding a specific IP, verify it exists on an interface (ip addr); otherwise bind 0.0.0.0 or fix the address.
- Privileged ports require CAP_NET_BIND_SERVICE; prefer a port > 1024.
- Give each co-located relay instance its own BUZZ_BIND_ADDR (and BUZZ_HEALTH_PORT).
Example fix
# before: Error: Failed to bind 0.0.0.0:3000: Address already in use (os error 98) BUZZ_BIND_ADDR=0.0.0.0:3000 # after BUZZ_BIND_ADDR=0.0.0.0:3001
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm the main bind address is bindable before boot.
fn bind_addr_available(addr: std::net::SocketAddr) -> bool {
std::net::TcpListener::bind(addr).is_ok()
}
let addr: std::net::SocketAddr = std::env::var("BUZZ_BIND_ADDR")
.unwrap_or_else(|_| "0.0.0.0:3000".into())
.parse()
.expect("BUZZ_BIND_ADDR must parse");
assert!(bind_addr_available(addr), "{addr} not bindable — in use, privileged, or not on an interface"); Type guard
fn classify_bind_error(e: &std::io::Error) -> &'static str {
match e.kind() {
std::io::ErrorKind::AddrInUse => "port in use",
std::io::ErrorKind::AddrNotAvailable => "IP not on any interface",
std::io::ErrorKind::PermissionDenied => "privileged port without CAP_NET_BIND_SERVICE",
_ => "other",
}
} Try / catch
let tcp_listener = match tokio::net::TcpListener::bind(&config.bind_addr).await {
Ok(l) => l,
Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => {
return Err(anyhow!("{} in use — change BUZZ_BIND_ADDR or stop the holder", config.bind_addr))
}
Err(e) => return Err(anyhow!("Failed to bind {}: {e}", config.bind_addr)),
}; Prevention
- Give co-located services distinct BUZZ_BIND_ADDR/BUZZ_HEALTH_PORT pairs in env files
- Verify any explicitly bound IP exists on an interface (ip addr) before deploying
- Prefer ports > 1024 or grant CAP_NET_BIND_SERVICE in the container spec
When it happens
Trigger: Another process on :3000 (web/dev tooling, a second relay); BUZZ_BIND_ADDR naming a cluster-internal or stale static IP that does not exist on any local interface (EADDRNOTAVAIL); binding a port < 1024 without capabilities; host-networked replicas with identical BUZZ_BIND_ADDR.
Common situations: Local dev with node/vite/another relay already on 3000; k8s manifests setting BUZZ_BIND_ADDR to a Service IP instead of a pod-local address; migrating to a node where the previously bound static IP no longer exists.
Related errors
- Failed to bind health port {}: {e}
- mesh endpoint bind on {} failed: {e}
- git conformance probe failed: {e}
- BUZZ_UDS_PATH {uds_path} exists but is not a socket
- Failed to bind UDS {uds_path}: {e}
AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16).
Data as JSON: /api/errors/2e3633783dc422ce.
Report an issue: GitHub.