loco-rs/loco · error

Failed to bind to address

Error message

Failed to bind to address

What it means

get_available_port binds a TcpListener to the TEST_BINDING_SERVER address with port 0 to let the OS pick a free port. If the bind fails — address family/hostname unresolvable, networking unavailable, or (on exotic setups) the resolver rejects it — the .expect panics with 'Failed to bind to address'.

Solutions

  1. Verify loopback networking works in the environment (ping/curl localhost, check /etc/hosts has 127.0.0.1 localhost).
  2. If TEST_BINDING_SERVER resolves oddly, ensure it maps to 127.0.0.1 (fix /etc/hosts or use an explicit IP).
  3. Enable networking in the CI container (remove --network none / add loopback support).
  4. Check seccomp/AppArmor or sandbox profiles that block socket() or bind() syscalls and allow them.
  5. As a fallback, parse the port from an explicitly bound 127.0.0.1:0 listener before calling the helper.

Example fix

// before
let addr = format!("{TEST_BINDING_SERVER}:0");
let listener = TcpListener::bind(addr).await.expect("Failed to bind to address");
// after
let addr = format!("{TEST_BINDING_SERVER}:0");
let listener = TcpListener::bind(&addr).await
    .or_else(|_| tokio::net::TcpListener::bind("127.0.0.1:0"))
    .await
    .expect("Failed to bind to any test address");
Defensive patterns

Strategy: try-catch

Validate before calling

// precheck before running tests
match tokio::net::TcpListener::bind("127.0.0.1:0").await {
    Ok(l) => drop(l),
    Err(e) => panic!("loopback bind unavailable in this environment: {e}"),
}

Try / catch

match TcpListener::bind(format!("{TEST_BINDING_SERVER}:0")).await {
    Ok(listener) => { /* proceed */ }
    Err(e) => eprintln!("skip test: cannot bind test listener: {e}"),
}

Prevention

When it happens

Trigger: Calling get_available_port() (directly or via the request test helpers) when TcpListener::bind("{TEST_BINDING_SERVER}:0") fails: the hostname does not resolve, IPv6/IPv4 mismatch in restricted containers, or no loopback networking (some sandboxes/CI without network namespaces configured).

Common situations: CI runners with networking disabled or restricted; Docker containers run with --network none; hostname resolution differences between macOS/Linux where 'localhost' resolution is blocked; corporate firewalls or seccomp profiles blocking socket bind.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of loco-rs/loco@23639d1e36 (2026-09-12). Data as JSON: /api/errors/3c95cf584b1870f0. Report an issue: GitHub.

Appendix: source

Thrown at src/testing/request.rs:157

/// The hostname to which the test server binds.
pub const TEST_BINDING_SERVER: &str = "localhost";

/// Constructs and returns the base URL used for the test server.
#[must_use]
pub fn get_base_url_port(port: i32) -> String {
    format!("http://{TEST_BINDING_SERVER}:{port}/")
}

/// Returns a unique port number. Usually increments by 1 starting from 59126
///
/// # Panics
///
/// Will panic if binding to test server address fails or if getting the local address fails
pub async fn get_available_port() -> i32 {
    let addr = format!("{TEST_BINDING_SERVER}:0");
    let listener = TcpListener::bind(addr)
        .await
        .expect("Failed to bind to address");

    i32::from(
        listener
            .local_addr()
            .expect("Failed to get local address")
            .port(),
    )
}

/// Bootstraps test application with test environment hard coded.
///
/// # Example
///
/// The provided example demonstrates how to boot the test case with the
/// application context.
///
/// ```rust,ignore
/// use myapp::app::App;

View on GitHub (pinned to 23639d1e36)