linera-io/linera-protocol · error · anyhow

Failed to start child server

Error message

Failed to start child server

What it means

Raised by `StorageService::wait_for_absence` (linera-storage-service child.rs) before spawning a child storage service. It polls `storage_service_check_absence` for the configured endpoint with increasing sleeps (1+2+...+9 ≈ 45s); if something is still serving on that endpoint after all attempts, it bails. In practice the endpoint is occupied by a leftover storage-service process (or previous child) that never shut down.

Source

Thrown at linera-storage-service/src/child.rs:49

    }

    fn command(&self) -> Command {
        let mut command = Command::new(&self.binary);
        command.args(["memory", "--endpoint", &self.endpoint]);
        command.kill_on_drop(true);
        command
    }

    /// Waits for the absence of the endpoint. If a child is terminated
    /// then it might take time to wait for its absence.
    async fn wait_for_absence(&self) -> Result<()> {
        for i in 1..10 {
            if storage_service_check_absence(&self.endpoint).await? {
                return Ok(());
            }
            linera_base::time::timer::sleep(Duration::from_secs(i)).await;
        }
        bail!("Failed to start child server");
    }

    /// Starts the storage service child process and returns a guard that keeps it alive.
    pub async fn run(&self) -> Result<StorageServiceGuard> {
        self.wait_for_absence().await?;
        let mut command = self.command();
        let child = command.spawn_into()?;
        let guard = StorageServiceGuard { _child: child };
        // We iterate until the child is spawned and can be accessed.
        // We add an additional waiting period to avoid problems.
        for i in 1..10 {
            let result = storage_service_check_validity(&self.endpoint).await;
            if result.is_ok() {
                return Ok(guard);
            }
            linera_base::time::timer::sleep(Duration::from_secs(i)).await;
        }
        bail!("Failed to start child server");

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Find and kill the process holding the endpoint (e.g. `lsof -i :7878` / `ss -ltnp` then kill, or `pkill -f linera-storage-service`)
  2. Use a unique endpoint/port per test run so services never collide
  3. If the previous child is legitimately still shutting down, allow more time or retry the run
  4. Make sure StorageServiceGuard values are dropped (or the test aborts cleanly) so children are killed between tests
Defensive patterns

Strategy: validation

Validate before calling

// Rust: verify the endpoint is free before StorageService::run
use tokio::net::TcpStream;

async fn endpoint_free(host: &str, port: u16) -> bool {
    TcpStream::connect((host, port)).await.is_err() // connectable => something is serving
}

if !endpoint_free("127.0.0.1", port).await {
    anyhow::bail!("endpoint {port} is occupied; kill the old storage service first");
}

Try / catch

match storage_service.run().await {
    Ok(guard) => guard,
    Err(e) if e.to_string().contains("Failed to start child server") => {
        // endpoint still occupied: kill the stale process, then retry once
        let _ = std::process::Command::new("pkill")
            .args(["-f", "linera-storage-service"])
            .status();
        storage_service.run().await?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `StorageService::run` on an endpoint where another storage service (from a previous test run, a crashed guard, or an orphaned process) is still listening and answering presence checks.

Common situations: Back-to-back e2e tests reusing the same endpoint without killing the previous service; a leaked child from a test that panicked before dropping its guard; a stale process from an aborted cargo test run.

Related errors


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