FuelLabs/fuel-core · error
Failed to bind to address {}: {}
Error message
Failed to bind to address {}: {} What it means
While starting the block aggregator API service, TcpIncoming::bind(addr) — the hyper/tonic listener used by the gRPC server — failed; the message includes the address and the OS error. Almost always the port is already in use, or the process lacks privileges to bind a port below 1024.
Source
Thrown at crates/services/block_aggregator_api/src/api/protobuf_adapter.rs:284
}
}
pub type APIService = ServiceRunner<UninitializedTask>;
pub fn incoming_and_server(
addr: SocketAddr,
) -> anyhow::Result<(TcpIncoming, tonic::transport::Server)> {
let tcp_nodelay = true;
let tcp_keepalive = None;
let accept_http1 = false;
let tonic_server = tonic::transport::Server::default()
.tcp_nodelay(tcp_nodelay)
.tcp_keepalive(tcp_keepalive)
.accept_http1(accept_http1);
let incoming = TcpIncoming::bind(addr)
.map_err(|e| anyhow::anyhow!("Failed to bind to address {}: {}", addr, e))?
.with_nodelay(Some(tcp_nodelay))
.with_keepalive(tcp_keepalive);
Ok((incoming, tonic_server))
}
pub fn new_service_with_custom_incoming<B>(
mut tonic_server: tonic::transport::Server,
incoming: TcpIncoming,
block_aggregator: B,
) -> anyhow::Result<APIService>
where
B: BlocksAggregatorApi,
{
let server = Server::new(block_aggregator);
let router = tonic_server.add_service(ProtoBlockAggregatorServer::new(server));
View on GitHub (pinned to b9d4d170da)
Solutions
- Identify the occupant with ss -ltnp (or lsof -i :<port>) and stop or reap that process.
- Change the configured bind address or port, or bind port 0 to let the OS assign a free port.
- For ports below 1024, grant the capability (setcap cap_net_bind_service) or switch to a high port.
- If a stale socket lingers in TIME_WAIT, wait it out or restart with address reuse enabled.
Example fix
// before let addr: SocketAddr = "0.0.0.0:4280".parse()?; let (incoming, server) = new_service_and_incoming(block_aggregator, addr).await?; // after: avoid the conflict let addr: SocketAddr = "0.0.0.0:4281".parse()?; // or pick a free port programmatically
Defensive patterns
Strategy: validation
Validate before calling
use std::net::TcpListener;
fn addr_bindable(addr: &std::net::SocketAddr) -> bool {
TcpListener::bind(addr).is_ok()
}
// before starting the block aggregator API
if !addr_bindable(&config.addr) {
return Err(anyhow::anyhow!("address {} is not bindable (in use or privileged)", config.addr));
} Try / catch
match new_service_and_incoming(aggregator, addr).await {
Err(e) if e.to_string().contains("Failed to bind") => {
// free the port (ss -ltnp | grep <port>), pick another port, or use port 0; then retry
}
rest => rest,
} Prevention
- Assign one well-known port per service and document it to avoid double-starts.
- Use port 0 in tests to get OS-assigned free ports.
- Add a bind pre-check in orchestration scripts before launching the service.
- Avoid privileged ports unless the process has cap_net_bind_service.
When it happens
Trigger: Starting the block aggregator API with a port held by another process (second node instance, leftover process, lingering socket), binding a privileged port as non-root, or an otherwise invalid or unavailable address.
Common situations: Running two aggregator instances with the same config; a crashed process leaving the socket occupied; container port mappings colliding; firewall or SELinux denying the bind.
Related errors
- Not implemented yet
- Missing code for contract: {id}
- Missing utxo for contract: {id}
- Missing column. Check the schema!
- No block height found
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/f6fba5f32b84583c.
Report an issue: GitHub.