tonhowtf/omniget · error

Relay closed connection unexpectedly

Error message

Relay closed connection unexpectedly

What it means

read_line wraps a TCP read from the relay; a return of 0 bytes means EOF — the relay peer closed the socket without sending the expected line. The function converts that silent EOF into an explicit error so callers don't loop forever on empty input.

Solutions

  1. Retry the operation, re-establishing the TCP connection to the relay
  2. Add keepalive/ping messages to the P2P protocol so idle connections aren't dropped
  3. Check relay server logs for crashes or restarts at the time of failure
  4. Increase relay-side idle timeouts for long transfers

Example fix

// before
let line = read_line(&mut reader).await?;
// after
let line = match read_line(&mut reader).await {
    Err(e) if e.to_string().contains("closed connection") => {
        reconnect_relay().await?;
        read_line(&mut reader).await?
    }
    other => other?,
};
Defensive patterns

Strategy: retry

Validate before calling

// check socket health before protocol steps
stream.set_keepalive(Some(Duration::from_secs(30)))?;

Try / catch

loop {
    match read_line(&mut reader).await {
        Err(e) if e.to_string().contains("closed connection") => {
            attempts += 1;
            if attempts > 3 { return Err(e); }
            reconnect().await?;
            continue;
        }
        other => break other,
    }
}

Prevention

When it happens

Trigger: The relay server (or the other P2P peer via the relay) closes the TCP connection while download or run_sender is waiting for the next protocol line.

Common situations: Relay crash or restart mid-transfer; intermediary/load-balancer idle timeout; the peer process exiting; firewall/NAT dropping a long-lived connection.

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 tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/4c1d71a0d9da5bf2. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/platforms/p2p/mod.rs:42

async fn connect_relay() -> anyhow::Result<TcpStream> {
    let addr = relay_addr();
    let stream = tokio::time::timeout(
        std::time::Duration::from_secs(10),
        TcpStream::connect(&addr),
    )
    .await
    .map_err(|_| anyhow!("Connection to relay timed out (10s)"))?
    .map_err(|e| anyhow!("Failed to connect to relay {}: {}", addr, e))?;
    Ok(stream)
}

async fn read_line(
    reader: &mut BufReader<tokio::io::ReadHalf<TcpStream>>,
) -> anyhow::Result<String> {
    let mut line = String::new();
    let n = reader.read_line(&mut line).await?;
    if n == 0 {
        anyhow::bail!("Relay closed connection unexpectedly");
    }
    Ok(line.trim_end().to_string())
}

fn check_relay_error(line: &str) -> anyhow::Result<()> {
    if let Some(err) = line.strip_prefix("ERROR ") {
        anyhow::bail!("Relay error: {}", err);
    }
    Ok(())
}

pub struct P2pDownloader;

impl P2pDownloader {
    pub fn new() -> Self {
        Self
    }
}

View on GitHub (pinned to 8600b91f42)