linera-io/linera-protocol · error

Failed to obtain a port

Error message

Failed to obtain a port

What it means

linera-base's get_free_port() asked the OS for a random free TCP port nine times (with growing 1..9s sleeps between attempts) and port_selector::random_free_tcp_port() returned None every time. The helper is only used by tests and dev tooling (test_notification_server, spawn_dummy_indexer/validator, etc.) to grab an ephemeral port for a locally spawned service.

Source

Thrown at linera-base/src/port.rs:20

// SPDX-License-Identifier: Apache-2.0

//! Functionality for obtaining some free port.

use anyhow::{bail, Result};
use port_selector::random_free_tcp_port;

use crate::time::Duration;

/// Provides a port that is currently not used
pub async fn get_free_port() -> Result<u16> {
    for i in 1..10 {
        let port = random_free_tcp_port();
        if let Some(port) = port {
            return Ok(port);
        }
        crate::time::timer::sleep(Duration::from_secs(i)).await;
    }
    bail!("Failed to obtain a port");
}

/// Provides a local endpoint that is currently available
pub async fn get_free_endpoint() -> Result<String> {
    let port = get_free_port().await?;
    Ok(format!("127.0.0.1:{port}"))
}

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Raise the file-descriptor limit for the test process: 'ulimit -n 65536' or set LimitNOFILE in the CI runner.
  2. Kill leaked listeners from previous runs: check 'ss -ltnp' / 'lsof -i' for stray linera processes and terminate them.
  3. Reduce parallelism of the test run (fewer concurrently spawned validators/indexers) so fewer ports are needed at once.
  4. Wait/retry the run: TIME_WAIT sockets expire (~60s), freeing the ephemeral range.

Example fix

// before
let port = linera_base::port::get_free_port().await?; // Fails when range is exhausted

// after (test harness): ensure fd headroom before spawning a fleet
// e.g. in CI: ulimit -n 65536; cargo test -- --test-threads=4
// and reuse one helper per suite instead of one port per test where possible.
Defensive patterns

Strategy: retry

Try / catch

// get_free_port already retries internally (9 attempts, backoff); at the harness level:
match linera_base::port::get_free_port().await {
    Ok(port) => port,
    Err(e) => {
        eprintln!("no free port: {e}; check `ulimit -n` and leaked listeners (`ss -ltnp`)");
        std::process::exit(1);
    }
}

Prevention

When it happens

Trigger: Running the Linera test suite or spawning many dummy validators/indexers on a machine where the ephemeral port range is exhausted (thousands of sockets in TIME_WAIT), the process hit its file-descriptor limit (ulimit -n), or the network stack is otherwise unable to allocate a bindable port.

Common situations: CI runners with low nofile limits running the full integration suite in parallel; a previous test run leaked listeners; heavy TIME_WAIT accumulation after repeated localhost connections; containers with restricted net.core settings.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/faa5cdf112a686b4. Report an issue: GitHub.